CubicSDR-0.2.3/000077500000000000000000000000001322677621400131245ustar00rootroot00000000000000CubicSDR-0.2.3/.gitignore000066400000000000000000000000321322677621400151070ustar00rootroot00000000000000build/ cmake_build/ dist/ CubicSDR-0.2.3/CMakeLists.txt000066400000000000000000001147141322677621400156740ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.8) project (CubicSDR) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") SET(CUBICSDR_VERSION_MAJOR "0") SET(CUBICSDR_VERSION_MINOR "2") SET(CUBICSDR_VERSION_PATCH "3") SET(CUBICSDR_VERSION_SUFFIX "") SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_SUFFIX}") SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}") SET(CPACK_PACKAGE_VERSION_MAJOR ${CUBICSDR_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${CUBICSDR_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${CUBICSDR_VERSION_PATCH}) SET (CUSTOM_BUILD OFF CACHE BOOL "Enable custom build options") # Build options for custom deploys, optimization and debugging IF(CUSTOM_BUILD) SET (CUBICSDR_BUILD_TITLE CACHE STRING "Custom Title") # bundle flags SET (CUBICSDR_INSTALL_NAME CACHE "CubicSDR" "Installation Name") SET (CUBICSDR_INSTALL_TITLE CACHE "CubicSDR" "Installation Title") SET (CUBICSDR_HEADER_IMAGE CACHE "" "Image file to display in header") SET (CUBICSDR_HEADER_BG CACHE "000000" "Background Color (HEX) for header") # feature flags SET (CUBICSDR_ENABLE_VIEW_DEMOD ON CACHE BOOL "Enable Second Demodulator Spectrum/Waterfall view.") SET (CUBICSDR_ENABLE_VIEW_SCOPE ON CACHE BOOL "Enable Demodulator Scope/Spectrum view.") SET (CUBICSDR_ENABLE_ABOUT_DIALOG ON CACHE BOOL "Enable About Dialog.") SET (CUBICSDR_DEFAULT_HIDE_BOOKMARKS OFF CACHE BOOL "Hide Bookmarks by Default.") SET (CUBICSDR_MODEM_EXCLUDE CACHE "" "Comma-separated list of modems to exclude.") IF (NOT CUBICSDR_HEADER_IMAGE STREQUAL "") SET(CUBICSDR_HAS_HEADER_IMAGE TRUE) GET_FILENAME_COMPONENT(CUBICSDR_HEADER_IMAGE_FILE "${CUBICSDR_HEADER_IMAGE}" NAME) GET_FILENAME_COMPONENT(CUBICSDR_HEADER_IMAGE_DIR "${CUBICSDR_HEADER_IMAGE}" PATH) ADD_DEFINITIONS( -DCUBICSDR_HEADER_IMAGE="${CUBICSDR_HEADER_IMAGE_FILE}" -DCUBICSDR_HEADER_BG="${CUBICSDR_HEADER_BG}" ) ENDIF() IF (NOT CUBICSDR_MODEM_EXCLUDE STREQUAL "") ADD_DEFINITIONS( -DCUBICSDR_MODEM_EXCLUDE="${CUBICSDR_MODEM_EXCLUDE}" ) ENDIF() ELSE() SET (CUBICSDR_BUILD_TITLE "CubicSDR v${CUBICSDR_VERSION} :: www.cubicsdr.com") # bundle flags SET (CUBICSDR_INSTALL_NAME "CubicSDR") SET (CUBICSDR_INSTALL_TITLE "CubicSDR ${CUBICSDR_VERSION} Installer") SET (CUBICSDR_HEADER_IMAGE "") SET (CUBICSDR_HEADER_BG "") # feature flags SET (CUBICSDR_ENABLE_VIEW_DEMOD ON) SET (CUBICSDR_ENABLE_VIEW_SCOPE ON) SET (CUBICSDR_DEFAULT_HIDE_BOOKMARKS OFF) SET (CUBICSDR_ENABLE_ABOUT_DIALOG ON) SET (CUBICSDR_EXCLUDE_MODEM "") ENDIF() IF(CUBICSDR_ENABLE_VIEW_DEMOD) ADD_DEFINITIONS( -DCUBICSDR_ENABLE_VIEW_DEMOD=1 ) ENDIF() IF(CUBICSDR_ENABLE_VIEW_SCOPE) ADD_DEFINITIONS( -DCUBICSDR_ENABLE_VIEW_SCOPE=1 ) ENDIF() IF(CUBICSDR_DEFAULT_HIDE_BOOKMARKS) ADD_DEFINITIONS( -DCUBICSDR_DEFAULT_HIDE_BOOKMARKS=1 ) ENDIF() IF(CUBICSDR_ENABLE_ABOUT_DIALOG) ADD_DEFINITIONS( -DCUBICSDR_ENABLE_ABOUT_DIALOG=1 ) ENDIF() ADD_DEFINITIONS( -DCUBICSDR_INSTALL_NAME="${CUBICSDR_INSTALL_NAME}" -DCUBICSDR_VERSION="${CUBICSDR_VERSION}" -DCUBICSDR_BUILD_TITLE="${CUBICSDR_BUILD_TITLE}" ) SET (ENABLE_DIGITAL_LAB OFF CACHE BOOL "Enable 'Digital Lab' testing features.") IF(ENABLE_DIGITAL_LAB) ADD_DEFINITIONS( -DENABLE_DIGITAL_LAB=1 ) ENDIF() set(USE_HAMLIB OFF CACHE BOOL "Support hamlib for radio control functions.") if (USE_HAMLIB) find_package(hamlib REQUIRED) if (NOT HAMLIB_FOUND) message(FATAL_ERROR "hamlib development files not found...") endif () include_directories(${HAMLIB_INCLUDE_DIR}) link_libraries(${HAMLIB_LIBRARY}) ADD_DEFINITIONS(-DUSE_HAMLIB) endif () macro(configure_files srcDir destDir globStr) message(STATUS "Copying ${srcDir}/${globStr} to directory ${destDir}") make_directory(${destDir}) file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/${globStr}) foreach(templateFile ${templateFiles}) set(srcTemplatePath ${srcDir}/${templateFile}) message(STATUS "Configuring file ${templateFile}") if(NOT IS_DIRECTORY ${srcTemplatePath}) configure_file( ${srcTemplatePath} ${destDir}/${templateFile} COPYONLY) endif(NOT IS_DIRECTORY ${srcTemplatePath}) endforeach(templateFile) endmacro(configure_files) macro(configure_files_recurse srcDir destDir) message(STATUS "Configuring directory ${destDir}") make_directory(${destDir}) file(GLOB_RECURSE templateFiles RELATIVE ${srcDir} ${srcDir}/*) foreach(templateFile ${templateFiles}) set(srcTemplatePath ${srcDir}/${templateFile}) message(STATUS "Configuring file ${templateFile}") if(NOT IS_DIRECTORY ${srcTemplatePath}) configure_file( ${srcTemplatePath} ${destDir}/${templateFile} COPYONLY) endif(NOT IS_DIRECTORY ${srcTemplatePath}) endforeach(templateFile) endmacro(configure_files_recurse) if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) MESSAGE( "64 bit compiler detected" ) SET( EX_PLATFORM 64 ) SET( EX_PLATFORM_NAME "x64" ) else( CMAKE_SIZEOF_VOID_P EQUAL 8 ) MESSAGE( "32 bit compiler detected" ) SET( EX_PLATFORM 32 ) SET( EX_PLATFORM_NAME "x86" ) endif( CMAKE_SIZEOF_VOID_P EQUAL 8 ) SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME}) SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME}) SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME}) IF (MSVC) include_directories ("${PROJECT_SOURCE_DIR}/external/wglext") SET(LIQUID_INCLUDES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/include/" CACHE STRING "Liquid-DSP include directory") SET(LIQUID_LIBRARIES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.lib" CACHE STRING "Liquid-DSP Library") SET(LIQUID_DLL "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.dll" CACHE STRING "Liquid-DSP DLL") SET(HAMLIB_DLLS "${PROJECT_SOURCE_DIR}/external/hamlib/${EX_PLATFORM}/libhamlib-2.dll" CACHE STRING "HAMLIB DLLS") ELSE (MSVC) ADD_DEFINITIONS( -std=c++0x -pthread ) ENDIF(MSVC) find_package(OpenGL REQUIRED) find_package(Liquid REQUIRED) include_directories(${LIQUID_INCLUDES}) SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} ${LIQUID_LIBRARIES}) find_package(wxWidgets COMPONENTS gl core propgrid adv base REQUIRED) set(wxWidgets_CONFIGURATION mswu) include(${wxWidgets_USE_FILE}) find_package(SoapySDR "0.4.0" NO_MODULE REQUIRED) include_directories(${SOAPY_SDR_INCLUDE_DIR}) SET(OTHER_LIBRARIES ${SOAPY_SDR_LIBRARY} ${OTHER_LIBRARIES}) ADD_DEFINITIONS( -DUSE_SOAPY_SDR=1 ) IF (WIN32) set(wxWidgets_USE_STATIC ON) set(BUILD_INSTALLER OFF CACHE BOOL "Build Installer") # Audio device selection is not mandatory, dummy audio device is used if none are compiled in. # Can also compile support for more than one simultaneously. set(USE_AUDIO_DS ON CACHE BOOL "Include support for DirectSound") set(USE_AUDIO_WASAPI OFF CACHE BOOL "Include support for WASAPI Audio") # TODO: # set(USE_AUDIO_ASIO OFF CACHE BOOL "Include support for ASIO Audio") # WASAPI IF(USE_AUDIO_WASAPI) ADD_DEFINITIONS(-D__WINDOWS_WASAPI__) IF (NOT MSVC) SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} -luuid -lksuser) ENDIF(NOT MSVC) ENDIF(USE_AUDIO_WASAPI) # DirectSound IF (USE_AUDIO_DS) ADD_DEFINITIONS(-D__WINDOWS_DS__) IF (MSVC) SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} dsound.lib) ELSE (MSVC) SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} -ldsound) ENDIF (MSVC) ENDIF(USE_AUDIO_DS) SET(USE_MINGW_PATCH OFF CACHE BOOL "Add some missing functions when compiling against mingw liquid-dsp.") IF (USE_MINGW_PATCH) SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} legacy_stdio_definitions.lib libgcc.a") ADD_DEFINITIONS( -DMINGW_PATCH=1 ) SET (GCC_LINKDIR "" CACHE STRING "") IF (GCC_LINKDIR) link_directories("${GCC_LINKDIR}") ENDIF() ENDIF() ENDIF (WIN32) MACRO(use_included_rtaudio) SET (cubicsdr_sources ${cubicsdr_sources} external/rtaudio/RtAudio.cpp ) SET (cubicsdr_headers ${cubicsdr_headers} external/rtaudio/RtAudio.h ) SOURCE_GROUP("_ext-RTAudio" REGULAR_EXPRESSION "external/rtaudio/.*${REG_EXT}") include_directories(${PROJECT_SOURCE_DIR}/external/rtaudio) ENDMACRO(use_included_rtaudio) IF (UNIX AND NOT APPLE) set(BUILD_DEB OFF CACHE BOOL "Build DEB") SET(USE_SYSTEM_RTAUDIO OFF CACHE BOOL "Use the system RtAudio which in turn provides OSS, ALSA, JACK, PulseAudio support depending on how it was compiled") SET(LIQUID_LIB liquid) SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} dl) IF(USE_SYSTEM_RTAUDIO) find_package(RtAudio) SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} ${RTAUDIO_LIBRARIES}) ADD_DEFINITIONS(${RTAUDIO_DEFINITIONS}) include_directories(${RTAUDIO_INCLUDE_DIR}) ELSE(USE_SYSTEM_RTAUDIO) use_included_rtaudio() SET(USE_AUDIO_PULSE ON CACHE BOOL "Use Pulse Audio") SET(USE_AUDIO_JACK OFF CACHE BOOL "Use Jack Audio") SET(USE_AUDIO_ALSA OFF CACHE BOOL "Use ALSA Audio") SET(USE_AUDIO_OSS OFF CACHE BOOL "Use OSS Audio") IF(USE_AUDIO_PULSE) SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} pulse-simple pulse) ADD_DEFINITIONS( -D__LINUX_PULSE__ ) ENDIF(USE_AUDIO_PULSE) IF(USE_AUDIO_JACK) find_package(Jack) SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} ${JACK_LIBRARIES}) ADD_DEFINITIONS( -D__UNIX_JACK__ ) include_directories(${JACK_INCLUDE_DIRS}) ENDIF(USE_AUDIO_JACK) IF(USE_AUDIO_ALSA) SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} asound) set(ALSA_INCLUDE_DIR "/usr/include" CACHE FILEPATH "ALSA include path") include_directories(${ALSA_INCLUDE_DIR}) set(ALSA_LIB_DIR "/usr/lib" CACHE FILEPATH "ALSA lib path") link_directories(${ALSA_LIB_DIR}) ADD_DEFINITIONS( -D__LINUX_ALSA__ ) ENDIF(USE_AUDIO_ALSA) IF(USE_AUDIO_OSS) SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} oss) ADD_DEFINITIONS( -D__LINUX_OSS__ ) ENDIF(USE_AUDIO_OSS) ENDIF(USE_SYSTEM_RTAUDIO) ELSE(UNIX AND NOT APPLE) use_included_rtaudio() ENDIF(UNIX AND NOT APPLE) IF (APPLE) SET(CMAKE_OSX_DEPLOYMENT_TARGET, "10.9") SET(LIQUID_LIB liquid) link_directories(/usr/local/lib) link_directories(/opt/local/lib) ADD_DEFINITIONS( -D__MACOSX_CORE__ ) FIND_LIBRARY(COREAUDIO_LIBRARY CoreAudio) SET (OTHER_LIBRARIES ${COREAUDIO_LIBRARY} ${OTHER_LIBRARIES}) set(BUNDLE_APP OFF CACHE BOOL "Bundle Application") ENDIF (APPLE) SET (cubicsdr_sources ${cubicsdr_sources} src/CubicSDR.cpp src/AppFrame.cpp src/AppConfig.cpp src/FrequencyDialog.cpp src/DemodLabelDialog.cpp src/IOThread.cpp src/ModemProperties.cpp src/BookmarkMgr.cpp src/sdr/SDRDeviceInfo.cpp src/sdr/SDRPostThread.cpp src/sdr/SDREnumerator.cpp src/sdr/SoapySDRThread.h src/demod/DemodulatorPreThread.cpp src/demod/DemodulatorThread.cpp src/demod/DemodulatorWorkerThread.cpp src/demod/DemodulatorInstance.cpp src/demod/DemodulatorMgr.cpp src/modules/modem/Modem.cpp src/modules/modem/ModemAnalog.cpp src/modules/modem/ModemDigital.cpp src/modules/modem/analog/ModemAM.cpp src/modules/modem/analog/ModemDSB.cpp src/modules/modem/analog/ModemFM.cpp src/modules/modem/analog/ModemNBFM.cpp src/modules/modem/analog/ModemFMStereo.cpp src/modules/modem/analog/ModemIQ.cpp src/modules/modem/analog/ModemLSB.cpp src/modules/modem/analog/ModemUSB.cpp src/audio/AudioThread.cpp src/audio/AudioSinkThread.cpp src/audio/AudioSinkFileThread.cpp src/audio/AudioFile.cpp src/audio/AudioFileWAV.cpp src/util/Gradient.cpp src/util/Timer.cpp src/util/MouseTracker.cpp src/util/GLExt.cpp src/util/GLFont.cpp src/util/DataTree.cpp src/panel/ScopePanel.cpp src/panel/SpectrumPanel.cpp src/panel/WaterfallPanel.cpp src/panel/MeterPanel.cpp src/panel/MeterPanel.h src/visual/ColorTheme.cpp src/visual/PrimaryGLContext.cpp src/visual/InteractiveCanvas.cpp src/visual/MeterCanvas.cpp src/visual/MeterContext.cpp src/visual/TuningCanvas.cpp src/visual/TuningContext.cpp src/visual/ModeSelectorCanvas.cpp src/visual/ModeSelectorContext.cpp src/visual/ScopeCanvas.cpp src/visual/ScopeContext.cpp src/visual/SpectrumCanvas.cpp src/visual/WaterfallCanvas.cpp src/visual/GainCanvas.cpp src/visual/ImagePanel.cpp src/process/VisualProcessor.cpp src/process/ScopeVisualProcessor.cpp src/process/SpectrumVisualProcessor.cpp src/process/FFTVisualDataThread.cpp src/process/FFTDataDistributor.cpp src/process/SpectrumVisualDataThread.cpp src/ui/GLPanel.cpp src/forms/SDRDevices/SDRDevices.cpp src/forms/SDRDevices/SDRDevicesForm.cpp src/forms/SDRDevices/SDRDeviceAdd.cpp src/forms/SDRDevices/SDRDeviceAddForm.cpp src/forms/Bookmark/BookmarkPanel.cpp src/forms/Bookmark/BookmarkView.cpp src/forms/Dialog/ActionDialogBase.cpp src/forms/Dialog/ActionDialog.cpp src/forms/Dialog/AboutDialogBase.cpp src/forms/Dialog/AboutDialog.cpp external/lodepng/lodepng.cpp external/tinyxml/tinyxml.cpp external/tinyxml/tinystr.cpp external/tinyxml/tinyxmlparser.cpp external/tinyxml/tinyxmlerror.cpp external/cubicvr2/math/cubic_math.cpp ) IF(ENABLE_DIGITAL_LAB) SET (cubicsdr_sources ${cubicsdr_sources} src/forms/DigitalConsole/DigitalConsole.cpp src/forms/DigitalConsole/DigitalConsoleFrame.cpp src/modules/modem/digital/ModemASK.cpp src/modules/modem/digital/ModemAPSK.cpp src/modules/modem/digital/ModemBPSK.cpp src/modules/modem/digital/ModemDPSK.cpp src/modules/modem/digital/ModemGMSK.cpp src/modules/modem/digital/ModemPSK.cpp src/modules/modem/digital/ModemOOK.cpp src/modules/modem/digital/ModemST.cpp src/modules/modem/digital/ModemSQAM.cpp src/modules/modem/digital/ModemQAM.cpp src/modules/modem/digital/ModemQPSK.cpp src/modules/modem/digital/ModemFSK.cpp ) ENDIF() SET (cubicsdr_headers ${cubicsdr_headers} src/CubicSDRDefs.h src/CubicSDR.h src/AppFrame.h src/AppConfig.h src/FrequencyDialog.h src/DemodLabelDialog.h src/IOThread.h src/ModemProperties.h src/BookmarkMgr.h src/sdr/SDRDeviceInfo.h src/sdr/SDRPostThread.h src/sdr/SDREnumerator.h src/sdr/SoapySDRThread.cpp src/demod/DemodulatorPreThread.h src/demod/DemodulatorThread.h src/demod/DemodulatorWorkerThread.h src/demod/DemodulatorInstance.h src/demod/DemodulatorMgr.h src/demod/DemodDefs.h src/modules/modem/Modem.h src/modules/modem/ModemAnalog.h src/modules/modem/ModemDigital.h src/modules/modem/analog/ModemAM.h src/modules/modem/analog/ModemDSB.h src/modules/modem/analog/ModemFM.h src/modules/modem/analog/ModemNBFM.h src/modules/modem/analog/ModemFMStereo.h src/modules/modem/analog/ModemIQ.h src/modules/modem/analog/ModemLSB.h src/modules/modem/analog/ModemUSB.h src/audio/AudioThread.h src/audio/AudioSinkThread.h src/audio/AudioSinkFileThread.h src/audio/AudioFile.h src/audio/AudioFileWAV.h src/util/Gradient.h src/util/Timer.h src/util/ThreadBlockingQueue.h src/util/MouseTracker.h src/util/GLExt.h src/util/GLFont.h src/util/DataTree.h src/panel/ScopePanel.h src/panel/SpectrumPanel.h src/panel/WaterfallPanel.h src/visual/ColorTheme.h src/visual/PrimaryGLContext.h src/visual/InteractiveCanvas.h src/visual/MeterCanvas.h src/visual/MeterContext.h src/visual/TuningCanvas.h src/visual/TuningContext.h src/visual/ModeSelectorCanvas.h src/visual/ModeSelectorContext.h src/visual/ScopeCanvas.h src/visual/ScopeContext.h src/visual/SpectrumCanvas.h src/visual/WaterfallCanvas.h src/visual/GainCanvas.h src/visual/ImagePanel.h src/process/VisualProcessor.h src/process/ScopeVisualProcessor.h src/process/SpectrumVisualProcessor.h src/process/FFTVisualDataThread.h src/process/FFTDataDistributor.h src/process/SpectrumVisualDataThread.h src/ui/GLPanel.h src/ui/UITestCanvas.cpp src/ui/UITestCanvas.h src/ui/UITestContext.cpp src/ui/UITestContext.h src/forms/SDRDevices/SDRDevices.h src/forms/SDRDevices/SDRDevicesForm.h src/forms/SDRDevices/SDRDeviceAdd.h src/forms/SDRDevices/SDRDeviceAddForm.h src/forms/Bookmark/BookmarkPanel.h src/forms/Bookmark/BookmarkView.h src/forms/Dialog/ActionDialogBase.h src/forms/Dialog/ActionDialog.h src/forms/Dialog/AboutDialogBase.h src/forms/Dialog/AboutDialog.h external/lodepng/lodepng.h external/tinyxml/tinyxml.h external/tinyxml/tinystr.h external/cubicvr2/math/aabb.h external/cubicvr2/math/cubic_math.h external/cubicvr2/math/cubic_types.h external/cubicvr2/math/frustum.h external/cubicvr2/math/mat3.h external/cubicvr2/math/mat4.h external/cubicvr2/math/plane.h external/cubicvr2/math/quaternion.h external/cubicvr2/math/sphere.h external/cubicvr2/math/transform.h external/cubicvr2/math/triangle.h external/cubicvr2/math/vec2.h external/cubicvr2/math/vec3.h external/cubicvr2/math/vec4.h ) IF(ENABLE_DIGITAL_LAB) SET (cubicsdr_headers ${cubicsdr_headers} src/forms/DigitalConsole/DigitalConsole.h src/forms/DigitalConsole/DigitalConsoleFrame.h src/modules/modem/digital/ModemASK.h src/modules/modem/digital/ModemAPSK.h src/modules/modem/digital/ModemBPSK.h src/modules/modem/digital/ModemDPSK.h src/modules/modem/digital/ModemGMSK.h src/modules/modem/digital/ModemPSK.h src/modules/modem/digital/ModemOOK.h src/modules/modem/digital/ModemST.h src/modules/modem/digital/ModemSQAM.h src/modules/modem/digital/ModemQAM.h src/modules/modem/digital/ModemQPSK.h src/modules/modem/digital/ModemFSK.h ) ENDIF() IF (USE_HAMLIB) SET (cubicsdr_headers ${cubicsdr_headers} src/forms/Dialog/PortSelectorDialogBase.h src/forms/Dialog/PortSelectorDialog.h src/rig/RigThread.h external/rs232/rs232.h ) SET (cubicsdr_sources ${cubicsdr_sources} src/forms/Dialog/PortSelectorDialogBase.cpp src/forms/Dialog/PortSelectorDialog.cpp src/rig/RigThread.cpp ) IF(WIN32) SET (cubicsdr_sources ${cubicsdr_sources} external/rs232/rs232-win.cpp ) ELSE() SET (cubicsdr_sources ${cubicsdr_sources} external/rs232/rs232-linux.cpp ) ENDIF() include_directories ( external/rs232/ ) ENDIF() SET (CUBICSDR_FONTS ${PROJECT_SOURCE_DIR}/font/vera_sans_mono12.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono16.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono18.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono24.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono27.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono32.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono36.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono48.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono64.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono72.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono96.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono12_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono16_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono18_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono24_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono27_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono32_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono36_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono48_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono64_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono72_0.png ${PROJECT_SOURCE_DIR}/font/vera_sans_mono96_0.png ) set(REG_EXT "[^/]*([.]cpp|[.]c|[.]h|[.]hpp)$") SOURCE_GROUP("Base" REGULAR_EXPRESSION "src/${REG_EXT}") SOURCE_GROUP("Forms\\SDRDevices" REGULAR_EXPRESSION "src/forms/SDRDevices/${REG_EXT}") SOURCE_GROUP("Forms\\Bookmark" REGULAR_EXPRESSION "src/forms/Bookmark/${REG_EXT}") SOURCE_GROUP("Forms\\Dialog" REGULAR_EXPRESSION "src/forms/Dialog/${REG_EXT}") SOURCE_GROUP("SDR" REGULAR_EXPRESSION "src/sdr/${REG_EXT}") IF(USE_HAMLIB) SOURCE_GROUP("Rig" REGULAR_EXPRESSION "src/rig/${REG_EXT}") SOURCE_GROUP("_ext-RS-232" REGULAR_EXPRESSION "external/rs232/${REG_EXT}") ENDIF() SOURCE_GROUP("Demodulator" REGULAR_EXPRESSION "src/demod/${REG_EXT}") SOURCE_GROUP("Modem" REGULAR_EXPRESSION "src/modules/modem/${REG_EXT}") SOURCE_GROUP("Modem\\Analog" REGULAR_EXPRESSION "src/modules/modem/analog/${REG_EXT}") IF(ENABLE_DIGITAL_LAB) SOURCE_GROUP("Modem\\Digital" REGULAR_EXPRESSION "src/modules/modem/digital/${REG_EXT}") SOURCE_GROUP("Forms\\DigitalConsole" REGULAR_EXPRESSION "src/forms/DigitalConsole/${REG_EXT}") ENDIF() SOURCE_GROUP("Audio" REGULAR_EXPRESSION "src/audio/${REG_EXT}") SOURCE_GROUP("Utility" REGULAR_EXPRESSION "src/util/${REG_EXT}") SOURCE_GROUP("Visual" REGULAR_EXPRESSION "src/visual/${REG_EXT}") SOURCE_GROUP("Panel" REGULAR_EXPRESSION "src/panel/${REG_EXT}") SOURCE_GROUP("Process" REGULAR_EXPRESSION "src/process/${REG_EXT}") SOURCE_GROUP("UI" REGULAR_EXPRESSION "src/ui/${REG_EXT}") SOURCE_GROUP("_ext-LodePNG" REGULAR_EXPRESSION "external/lodepng/.*${REG_EXT}") SOURCE_GROUP("_ext-TinyXML" REGULAR_EXPRESSION "external/tinyxml/.*${REG_EXT}") SOURCE_GROUP("_ext-CubicVR2" REGULAR_EXPRESSION "external/cubicvr2/.*${REG_EXT}") include_directories ( ${PROJECT_SOURCE_DIR}/src/forms/SDRDevices ${PROJECT_SOURCE_DIR}/src/forms/DigitalConsole ${PROJECT_SOURCE_DIR}/src/forms/Bookmark ${PROJECT_SOURCE_DIR}/src/forms/Dialog ${PROJECT_SOURCE_DIR}/src/sdr ${PROJECT_SOURCE_DIR}/src/demod ${PROJECT_SOURCE_DIR}/src/modules ${PROJECT_SOURCE_DIR}/src/modules/modem ${PROJECT_SOURCE_DIR}/src/modules/modem/digital ${PROJECT_SOURCE_DIR}/src/modules/modem/analog ${PROJECT_SOURCE_DIR}/src/audio ${PROJECT_SOURCE_DIR}/src/util ${PROJECT_SOURCE_DIR}/src/panel ${PROJECT_SOURCE_DIR}/src/visual ${PROJECT_SOURCE_DIR}/src/process ${PROJECT_SOURCE_DIR}/src/ui ${PROJECT_SOURCE_DIR}/src/rig ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/external/lodepng ${PROJECT_SOURCE_DIR}/external/tinyxml ${PROJECT_SOURCE_DIR}/external/cubicvr2/math ) set(RES_FILES "") if(MINGW OR MSVC) set(RES_FILES ${PROJECT_SOURCE_DIR}/cubicsdr.rc) set(CMAKE_RC_COMPILER_INIT windres) ENABLE_LANGUAGE(RC) IF(EX_PLATFORM EQUAL 64) SET(RC_TARGET "pe-x86-64") ELSE(EX_PLATFORM EQUAL 64) SET(RC_TARGET "pe-i386") ENDIF(EX_PLATFORM EQUAL 64) SET(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") endif(MINGW OR MSVC) IF (NOT BUNDLE_APP) configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/fonts "*.fnt") configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/fonts "*.png") configure_files(${PROJECT_SOURCE_DIR}/icon ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} CubicSDR.ico) IF(MSVC) configure_files(${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") ENDIF() IF (CUBICSDR_HAS_HEADER_IMAGE) configure_files(${CUBICSDR_HEADER_IMAGE_DIR} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} ${CUBICSDR_HEADER_IMAGE_FILE}) ENDIF() add_executable(CubicSDR ${cubicsdr_sources} ${cubicsdr_headers} ${RES_FILES}) target_link_libraries(CubicSDR ${LIQUID_LIB} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${OTHER_LIBRARIES}) ENDIF (NOT BUNDLE_APP) IF (MSVC) set_target_properties(CubicSDR PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS") set_target_properties(CubicSDR PROPERTIES COMPILE_DEFINITIONS_DEBUG "_WINDOWS") set_target_properties(CubicSDR PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS") set_target_properties(CubicSDR PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_WINDOWS") set_target_properties(CubicSDR PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") set_target_properties(CubicSDR PROPERTIES COMPILE_DEFINITIONS_RELEASE "_WINDOWS") set_target_properties(CubicSDR PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") set_target_properties(CubicSDR PROPERTIES COMPILE_DEFINITIONS_MINSIZEREL "_WINDOWS") set(CMAKE_CREATE_WIN32_EXE "/SUBSYSTEM:WINDOWS /ENTRY:\"mainCRTStartup\"") set_target_properties (CubicSDR PROPERTIES OUTPUT_NAME "${CUBICSDR_INSTALL_NAME}") ADD_DEFINITIONS( /wd\"4996\" ) ENDIF(MSVC) IF (APPLE) ADD_DEFINITIONS( -DHAVE_TYPE_TRAITS=1 -mmacosx-version-min=10.9 ) ENDIF(APPLE) IF (APPLE AND BUNDLE_APP) PROJECT(CubicSDR) set(CPACK_BINARY_DRAGNDROP ON) SET(MACOSX_BUNDLE_BUNDLE_NAME CubicSDR) SET(CPACK_APPLE_BUNDLE_ID "com.cubicproductions.cubicsdr") set(CUBICSDR_CODE_SIGN OFF CACHE BOOL "Code Signing") set(BUNDLE_SOAPY_MODS OFF CACHE BOOL "Bundle local SoapySDR modules") set(BUNDLE_MIR_SDR OFF CACHE BOOL "Bundle mir_sdr for personal use only -- do not distribute.") IF (BUNDLE_SOAPY_MODS) ADD_DEFINITIONS( -DBUNDLE_SOAPY_MODS=1 ) set(BUNDLED_MODS_ONLY OFF CACHE BOOL "Use bundled mods only") IF (BUNDLED_MODS_ONLY) ADD_DEFINITIONS( -DBUNDLED_MODS_ONLY=1 ) ENDIF() ENDIF() ADD_DEFINITIONS( -std=c++0x -pthread -D_OSX_APP_ ) ADD_EXECUTABLE(CubicSDR MACOSX_BUNDLE ${cubicsdr_sources} ${cubicsdr_headers} ${CUBICSDR_FONTS} ${PROJECT_SOURCE_DIR}/icon/CubicSDR.icns ) SET_SOURCE_FILES_PROPERTIES( ${PROJECT_SOURCE_DIR}/icon/CubicSDR.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) SET_SOURCE_FILES_PROPERTIES( ${CUBICSDR_FONTS} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/fonts ) target_link_libraries(CubicSDR ${LIQUID_LIB} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${OTHER_LIBRARIES}) SET_TARGET_PROPERTIES(CubicSDR PROPERTIES MACOSX_BUNDLE TRUE MACOSX_BUNDLE_INFO_STRING "CubicSDR Open-Source Software-Defined Radio Application" MACOSX_BUNDLE_BUNDLE_NAME "CubicSDR" MACOSX_BUNDLE_BUNDLE_VERSION "${CUBICSDR_VERSION}" MACOSX_BUNDLE_LONG_VERSION_STRING "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_SUFFIX}" MACOSX_BUNDLE_SHORT_VERSION_STRING "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}" MACOSX_BUNDLE_GUI_IDENTIFIER "com.cubicproductions.cubicsdr" MACOSX_BUNDLE_ICON_FILE "CubicSDR.icns" MACOSX_BUNDLE_COPYRIGHT "Copyright 2015 Charles J. Cliffe. All Rights Reserved." ) SET(APPS "${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/CubicSDR.app") # SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) IF (BUNDLE_SOAPY_MODS) message(STATUS "SOAPY_ROOT: ${SOAPY_SDR_ROOT}") SET(SOAPY_SDR_MOD_PATH "${SOAPY_SDR_ROOT}/lib/SoapySDR/modules${SOAPY_SDR_ABI_VERSION}") message(STATUS "SOAPY_SDR_MOD_PATH: ${SOAPY_SDR_MOD_PATH}") file(GLOB SOAPY_MODS ${SOAPY_SDR_MOD_PATH}/*.so) FOREACH(SOAPY_MOD_FILE ${SOAPY_MODS}) INSTALL( FILES "${SOAPY_MOD_FILE}" DESTINATION "${APPS}/Contents/MacOS/modules" COMPONENT Runtime ) ENDFOREACH() ENDIF(BUNDLE_SOAPY_MODS) SET_TARGET_PROPERTIES(CubicSDR PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/cmake/CubicSDRInfo.plist.in") INSTALL(CODE " SET(BU_COPY_FULL_FRAMEWORK_CONTENTS ON) include(BundleUtilities) fixup_bundle(\"${APPS}\" \"\" \"/usr/local/lib\") " COMPONENT Runtime) IF (BUNDLE_SOAPY_MODS) FOREACH(SOAPY_MOD_FILE ${SOAPY_MODS}) GET_FILENAME_COMPONENT(SOAPY_MOD_NAME ${SOAPY_MOD_FILE} NAME) IF(NOT BUNDLE_MIR_SDR) # prevent inclusion of libmirsdrapi-rsp.so IF(${SOAPY_MOD_NAME} STREQUAL "libsdrPlaySupport.so") message(STATUS "Excluding libsdrPlaySupport.so") CONTINUE() ELSE() message(STATUS "Bundling ${SOAPY_MOD_NAME} from ${SOAPY_MOD_FILE}") ENDIF() ENDIF() INSTALL(CODE " fixup_bundle(\"${APPS}\" \"${APPS}/Contents/MacOS/modules/${SOAPY_MOD_NAME}\" \"/usr/local/lib\") " COMPONENT Runtime) ENDFOREACH() ENDIF(BUNDLE_SOAPY_MODS) IF (CUBICSDR_CODE_SIGN) SET (CUBICSDR_CERT "3rd Party Mac Developer Application: [Name]" CACHE STRING "Code signing certificate name.") MESSAGE(STATUS "Code Signing Enabled: ${CUBICSDR_CERT}") CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/cmake/code_sign.sh.in" "${CMAKE_CURRENT_BINARY_DIR}/code_sign.sh" ) CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/cmake/dmg_sign.sh.in" "${CMAKE_CURRENT_BINARY_DIR}/dmg_sign.sh" ) ADD_CUSTOM_COMMAND( TARGET CubicSDR COMMAND chmod ARGS +x ${CMAKE_CURRENT_BINARY_DIR}/code_sign.sh ${CMAKE_CURRENT_BINARY_DIR}/dmg_sign.sh ) INSTALL(CODE " execute_process( COMMAND ${CMAKE_COMMAND} -E echo \"Signing code..\" COMMAND \"${CMAKE_CURRENT_BINARY_DIR}/code_sign.sh\" )" COMPONENT Runtime) ADD_CUSTOM_COMMAND( TARGET CubicSDR POST_BUILD COMMAND ${CMAKE_CPACK_COMMAND} COMMAND ${CMAKE_COMMAND} -E echo "Signing package.." COMMAND "${CMAKE_CURRENT_BINARY_DIR}/dmg_sign.sh" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) ENDIF(CUBICSDR_CODE_SIGN) INSTALL(CODE " VERIFY_APP(\"${APPS}\") " COMPONENT Runtime) INSTALL(TARGETS CubicSDR BUNDLE DESTINATION . COMPONENT Runtime ) include(CPack) ENDIF() IF(APPLE AND NOT BUNDLE_APP) IF (NOT CMAKE_INSTALL_PREFIX) SET(CMAKE_INSTALL_PREFIX "/usr/") ENDIF() ADD_DEFINITIONS( -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" ) set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro") INSTALL(TARGETS CubicSDR DESTINATION bin) install(FILES ${PROJECT_SOURCE_DIR}/src/CubicSDR.png DESTINATION share/cubicsdr) install(FILES ${CUBICSDR_FONTS} DESTINATION share/cubicsdr/fonts) CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @ONLY IMMEDIATE) ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") ENDIF () IF (WIN32 AND NOT BUILD_INSTALLER) ADD_DEFINITIONS( -DRES_FOLDER="../share/cubicsdr/" ) INSTALL(TARGETS CubicSDR DESTINATION bin) INSTALL(FILES ${LIQUID_DLL} DESTINATION bin) IF(USE_HAMLIB) FOREACH(HAMLIB_DLL ${HAMLIB_DLLS}) message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}") INSTALL( FILES "${HAMLIB_DLL}" DESTINATION bin ) ENDFOREACH() ENDIF() INSTALL(FILES ${PROJECT_SOURCE_DIR}/src/CubicSDR.png DESTINATION share/cubicsdr) INSTALL(FILES ${CUBICSDR_FONTS} DESTINATION share/cubicsdr/fonts) IF (CUBICSDR_HAS_HEADER_IMAGE) INSTALL(FILES ${CUBICSDR_HEADER_IMAGE} DESTINATION share/cubicsdr/) ENDIF() ENDIF() IF (WIN32 AND BUILD_INSTALLER) set(BUNDLE_SOAPY_MODS OFF CACHE BOOL "Bundle local SoapySDR modules") set(CPACK_GENERATOR NSIS) set(CPACK_PACKAGE_NAME "${CUBICSDR_INSTALL_NAME}") set(CPACK_NSIS_DISPLAY_NAME "${CUBICSDR_INSTALL_TITLE}") set(CPACK_PACKAGE_VENDOR "cubicsdr.com") set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CUBICSDR_INSTALL_NAME}") SET(CPACK_NSIS_INSTALLED_ICON_NAME "CubicSDR.ico") SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/icon\\\\NSIS_Header.bmp") IF(EX_PLATFORM EQUAL 64) SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") SET(CPACK_NSIS_PACKAGE_NAME "${CUBICSDR_INSTALL_NAME}") SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CUBICSDR_INSTALL_NAME} ${CPACK_PACKAGE_VERSION}") set(CMAKE_CL_64 TRUE) # This gets around a bug in the CPack installer name generation for MinGW 64-bit since 2.8 ELSE(EX_PLATFORM EQUAL 64) SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") SET(CPACK_NSIS_PACKAGE_NAME "${CUBICSDR_INSTALL_NAME} (x86)") SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CUBICSDR_INSTALL_NAME} ${CPACK_PACKAGE_VERSION} (x86)") set(CMAKE_CL_64 FALSE) ENDIF(EX_PLATFORM EQUAL 64) set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") install(TARGETS CubicSDR RUNTIME DESTINATION .) install(FILES ${PROJECT_SOURCE_DIR}/icon/CubicSDR.ico ${LIQUID_DLL} DESTINATION .) install(FILES ${CUBICSDR_FONTS} DESTINATION fonts) IF (CUBICSDR_HAS_HEADER_IMAGE) INSTALL(FILES ${CUBICSDR_HEADER_IMAGE} DESTINATION .) ENDIF() IF(USE_HAMLIB) FOREACH(HAMLIB_DLL ${HAMLIB_DLLS}) message(STATUS "Copying Hamlib DLL: ${HAMLIB_DLL}") INSTALL( FILES ${HAMLIB_DLL} DESTINATION .) ENDFOREACH() ENDIF() IF (BUNDLE_SOAPY_MODS) ADD_DEFINITIONS( -DBUNDLE_SOAPY_MODS=1 ) set(BUNDLED_MODS_ONLY OFF CACHE BOOL "Use bundled mods only") IF (BUNDLED_MODS_ONLY) ADD_DEFINITIONS( -DBUNDLED_MODS_ONLY=1 ) ENDIF() file(GLOB SOAPY_BINS ${SOAPY_SDR_ROOT}/bin/*.dll) file(GLOB SOAPY_MODS ${SOAPY_SDR_ROOT}/lib/SoapySDR/modules${SOAPY_SDR_ABI_VERSION}/*.dll) message(STATUS "SOAPY_BINS: ${SOAPY_BINS}") message(STATUS "SOAPY_MODS: ${SOAPY_MODS}") install(FILES ${SOAPY_BINS} DESTINATION .) install(FILES ${SOAPY_MODS} DESTINATION modules) ENDIF(BUNDLE_SOAPY_MODS) IF(MSVC AND EX_PLATFORM EQUAL 32) file(GLOB MSVC32_DEPS ${PROJECT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/*.dll) install(FILES ${MSVC32_DEPS} DESTINATION .) ENDIF(MSVC AND EX_PLATFORM EQUAL 32) set(CPACK_PACKAGE_EXECUTABLES CubicSDR "CubicSDR") IF (MSVC) install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/vc_redist.${EX_PLATFORM_NAME}.exe DESTINATION vc_redist) set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\vc_redist\\\\vc_redist.${EX_PLATFORM_NAME}.exe\\\" /passive /norestart'") ENDIF (MSVC) INCLUDE(CPack) ENDIF (WIN32 AND BUILD_INSTALLER) IF (UNIX AND NOT APPLE AND BUILD_DEB) set(CPACK_GENERATOR DEB) set(CPACK_PACKAGE_NAME "CubicSDR") SET(CPACK_DEBIAN_PACKAGE_DEPENDS " libwxgtk3.0-0, libpulse0") SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Charles J. Cliffe ") SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "CubicSDR Software Defined Radio application v${CUBICSDR_VERSION}") SET(CPACK_DEBIAN_PACKAGE_SECTION "comm") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${EX_PLATFORM_NAME}") IF (NOT CMAKE_INSTALL_PREFIX) SET(CMAKE_INSTALL_PREFIX "/usr/") ENDIF() ADD_DEFINITIONS( -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" -D_FORTIFY_SOURCE=2 ) set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro") CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/external/deb/deb_post.sh.in" "${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in" "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE) INSTALL(TARGETS CubicSDR DESTINATION bin) install(FILES ${PROJECT_SOURCE_DIR}/src/CubicSDR.png DESTINATION share/cubicsdr) install(FILES ${CUBICSDR_FONTS} DESTINATION share/cubicsdr/fonts) INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" DESTINATION share/applications) INCLUDE(CPack) ENDIF() IF(UNIX AND NOT APPLE AND NOT BUILD_DEB) IF (NOT CMAKE_INSTALL_PREFIX) SET(CMAKE_INSTALL_PREFIX "/usr/") ENDIF() ADD_DEFINITIONS( -DRES_FOLDER="${CMAKE_INSTALL_PREFIX}/share/cubicsdr/" ) set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro") CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CubicSDR.desktop.in" "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" @ONLY IMMEDIATE) INSTALL(TARGETS CubicSDR DESTINATION bin) INSTALL(FILES ${PROJECT_SOURCE_DIR}/src/CubicSDR.png DESTINATION share/cubicsdr) INSTALL(FILES ${CUBICSDR_FONTS} DESTINATION share/cubicsdr/fonts) IF (CUBICSDR_HAS_HEADER_IMAGE) INSTALL(FILES ${CUBICSDR_HEADER_IMAGE} DESTINATION share/cubicsdr) ENDIF() INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/CubicSDR.desktop" DESTINATION share/applications) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/external/deb/deb_post.sh.in" "${CMAKE_CURRENT_BINARY_DIR}/deb_post.sh" @ONLY IMMEDIATE) CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @ONLY IMMEDIATE) ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") ENDIF() CubicSDR-0.2.3/LICENSE000066400000000000000000000432541322677621400141410ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. CubicSDR-0.2.3/README.md000066400000000000000000000042071322677621400144060ustar00rootroot00000000000000CubicSDR ======== Cross-Platform Software-Defined Radio Application - Please see the [CubicSDR GitHub Wiki](https://github.com/cjcliffe/CubicSDR/wiki) for build instructions. - Manual is available (work-in-progress) at [cubicsdr.readthedocs.io](http://cubicsdr.readthedocs.io). - See also the current [CubicSDR Releases](https://github.com/cjcliffe/CubicSDR/releases) page for available binaries. Utilizes: -------- - liquid-dsp (http://liquidsdr.org/ -- https://github.com/jgaeddert/liquid-dsp) - SoapySDR (http://www.pothosware.com/ -- https://github.com/pothosware/SoapySDR) - RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ -- http://github.com/thestk/rtaudio/) - LodePNG (http://lodev.org/lodepng/) - BMFont (http://www.angelcode.com/ -- http://www.angelcode.com/products/bmfont/) - Bitstream Vera font (http://en.wikipedia.org/wiki/Bitstream_Vera) - OpenGL (https://www.opengl.org/) - wxWidgets (https://www.wxwidgets.org/) - CMake (http://www.cmake.org/) Optional Libs: -------- - FFTW3 (can be compiled into liquid-dsp if desired) (http://www.fftw.org/ -- https://github.com/FFTW/fftw3) - hamlib (https://sourceforge.net/p/hamlib/wiki/Hamlib/ -- https://sourceforge.net/p/hamlib/code/ci/master/tree/) Features and Status: -------------------- - Please see the issues on GitHub or visit https://github.com/cjcliffe/CubicSDR/wiki/CubicSDR-Roadmap-and-Ideas for more information. - A manual is in development at https://github.com/cjcliffe/CubicSDR/issues/248 if you would like to contribute. Recommended minimum requirements: -------------------- - Multi-core processor system with at least 1GB RAM. - Graphics card with at least 128MB video memory and OpenGL 3.x or ES 2.0 support. - OSX 10.9+ for Mac binary releases. - Windows 7+ for 64 or 32-bit Windows binary releases. - Linux and other embedded distribution support yet to be indexed, known to at least work on Debian 8+ and Ubuntu 14+. - Raspberry Pi2 support is being experimented with; earlier versions of CubicSDR known to work with Banana Pi. Target Platforms: ---------------- - [x] OSX - [x] Windows - [x] Linux - [ ] HTML5 License: ------- - GPL-2.0+ CubicSDR-0.2.3/cmake/000077500000000000000000000000001322677621400142045ustar00rootroot00000000000000CubicSDR-0.2.3/cmake/CubicSDR.desktop.in000066400000000000000000000003661322677621400176070ustar00rootroot00000000000000[Desktop Entry] Type=Application Exec=CubicSDR %u Icon=@CMAKE_INSTALL_PREFIX@/share/cubicsdr/CubicSDR.png Terminal=false Name=CubicSDR GenericName=CubicSDR Comment=Software-Defined Radio Application Categories=Science;HamRadio;DataVisualization; CubicSDR-0.2.3/cmake/CubicSDRInfo.plist.in000066400000000000000000000025271322677621400201060ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${MACOSX_BUNDLE_EXECUTABLE_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion ${MACOSX_BUNDLE_BUNDLE_VERSION} CSResourcesFileMapped LSRequiresCarbon NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} NSHighResolutionCapable NSSupportsAutomaticGraphicsSwitching CubicSDR-0.2.3/cmake/Modules/000077500000000000000000000000001322677621400156145ustar00rootroot00000000000000CubicSDR-0.2.3/cmake/Modules/FindJack.cmake000066400000000000000000000017341322677621400202740ustar00rootroot00000000000000# Try to find JACK # This will define the following variables: # # JACK_FOUND - Whether Jack was found. # JACK_INCLUDE_DIRS - Jack include directories. # JACK_LIBRARIES - Jack libraries. include(FindPackageHandleStandardArgs) if(JACK_LIBRARIES AND JACK_INCLUDE_DIRS) # in cache already set(JACK_FOUND TRUE) else() find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(_JACK jack) endif(PKG_CONFIG_FOUND) find_path(JACK_INCLUDE_DIR NAMES jack/jack.h PATHS ${_JACK_INCLUDEDIR} ) find_library(JACK_LIBRARY NAMES jack PATHS ${_JACK_LIBDIR} ) set(JACK_INCLUDE_DIRS ${JACK_INCLUDE_DIR} ) set(JACK_LIBRARIES ${JACK_LIBRARY} ) find_package_handle_standard_args(Jack DEFAULT_MSG JACK_LIBRARIES JACK_INCLUDE_DIRS) # show the JACK_INCLUDE_DIRS and JACK_LIBRARIES variables only in the advanced view mark_as_advanced(JACK_INCLUDE_DIR JACK_LIBRARY JACK_INCLUDE_DIRS JACK_LIBRARIES) endif() CubicSDR-0.2.3/cmake/Modules/FindLiquid.cmake000066400000000000000000000013071322677621400206470ustar00rootroot00000000000000# - Find LIQUID # Find the native LIQUID includes and library # # LIQUID_INCLUDES - where to find LIQUID.h # LIQUID_LIBRARIES - List of libraries when using LIQUID. # LIQUID_FOUND - True if LIQUID found. if (LIQUID_INCLUDES) # Already in cache, be silent set (LIQUID_FIND_QUIETLY TRUE) endif (LIQUID_INCLUDES) find_path (LIQUID_INCLUDES liquid/liquid.h) find_library (LIQUID_LIBRARIES NAMES liquid) # handle the QUIETLY and REQUIRED arguments and set LIQUID_FOUND to TRUE if # all listed variables are TRUE include (FindPackageHandleStandardArgs) find_package_handle_standard_args (LIQUID DEFAULT_MSG LIQUID_LIBRARIES LIQUID_INCLUDES) #mark_as_advanced (LIQUID_LIBRARIES LIQUID_INCLUDES) CubicSDR-0.2.3/cmake/Modules/FindRtAudio.cmake000066400000000000000000000023631322677621400207720ustar00rootroot00000000000000# https://github.com/idiap/juicer/blob/master/cmake/FindRtAudio.cmake # # Copyright 2015 by Idiap Research Institute # # Licensed under 3-clause BSD. # # Author(s): # Phil Garner, November 2015 # # ...but basically copied from FindSndFile in libube, in turn from the examples # on the web. # # # Try to find RtAudio # Once done this will define # RTAUDIO_FOUND - System has RtAudio # RTAUDIO_INCLUDE_DIR - The RtAudio include directories # RTAUDIO_LIBRARIES - The libraries needed to use RtAudio # RTAUDIO_DEFINITIONS - Compiler switches required for using RtAudio # RTAUDIO_VERSION_STRING - the version of RtAudio found # find_package(PkgConfig) pkg_check_modules(PC_RTAUDIO rtaudio) set(RTAUDIO_DEFINITIONS ${PC_RTAUDIO_CFLAGS_OTHER}) set(RTAUDIO_VERSION_STRING ${PC_RTAUDIO_VERSION}) find_path( RTAUDIO_INCLUDE_DIR RtAudio.h HINTS ${PC_RTAUDIO_INCLUDEDIR} ${PC_RTAUDIO_INCLUDE_DIRS} ) find_library( RTAUDIO_LIBRARIES NAMES rtaudio HINTS ${PC_RTAUDIO_LIBDIR} ${PC_RTAUDIO_LIBRARY_DIRS} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( RtAudio REQUIRED_VARS RTAUDIO_LIBRARIES RTAUDIO_INCLUDE_DIR VERSION_VAR RTAUDIO_VERSION_STRING ) #mark_as_advanced(RTAUDIO_INCLUDE_DIR RTAUDIO_LIBRARIES) CubicSDR-0.2.3/cmake/Modules/Findhamlib.cmake000066400000000000000000000037071322677621400206620ustar00rootroot00000000000000# - Try to find Hamlib # Author: George L. Emigh - AB4BD # # Change Log: Charles J. Cliffe # Updates: # Jan 2015 - Add /opt/ paths for OSX MacPorts # - Fix HAMLIB_INCLUDE_DIR absolute search # - Add static lib support # TODO: # Windows support # # HAMLIB_FOUND - system has Hamlib # HAMLIB_LIBRARY - location of the library for hamlib # HAMLIB_INCLUDE_DIR - location of the include files for hamlib set(HAMLIB_FOUND FALSE) find_path(HAMLIB_INCLUDE_DIR NAMES hamlib/rig.h PATHS /usr/include /usr/local/include /opt/local/include ) find_library(HAMLIB_LIBRARY NAMES hamlib PATHS /usr/lib64/hamlib /usr/lib/hamlib /usr/lib64 /usr/lib /usr/local/lib64/hamlib /usr/local/lib/hamlib /usr/local/lib64 /usr/local/lib /opt/local/lib /opt/local/lib/hamlib ) find_library(HAMLIB_STATIC_LIBRARY NAMES libhamlib.a PATHS /usr/lib64/hamlib /usr/lib/hamlib /usr/lib64 /usr/lib /usr/local/lib64/hamlib /usr/local/lib/hamlib /usr/local/lib64 /usr/local/lib /opt/local/lib /opt/local/lib/hamlib ) GET_FILENAME_COMPONENT(HAMLIB_LIB_FOLDER ${HAMLIB_STATIC_LIBRARY} DIRECTORY) file(GLOB HAMLIB_STATIC_MODS ${HAMLIB_LIB_FOLDER}/hamlib-*.a) if(HAMLIB_INCLUDE_DIR AND HAMLIB_LIBRARY) set(HAMLIB_FOUND TRUE) # message(STATUS "Hamlib version: ${VERSION}") message(STATUS "Found hamlib library at: ${HAMLIB_LIBRARY}") message(STATUS "Found hamlib static library at: ${HAMLIB_STATIC_LIBRARY}") message(STATUS "Found hamlib static modules: ${HAMLIB_STATIC_MODS}") message(STATUS "Found hamlib include directory at: ${HAMLIB_INCLUDE_DIR}") endif(HAMLIB_INCLUDE_DIR AND HAMLIB_LIBRARY) IF(NOT HAMLIB_FOUND) IF(NOT HAMLIB_FIND_QUIETLY) MESSAGE(STATUS "HAMLIB was not found.") ELSE(NOT HAMLIB_FIND_QUIETLY) IF(HAMLIB_FIND_REQUIRED) MESSAGE(FATAL_ERROR "HAMLIB was not found.") ENDIF(HAMLIB_FIND_REQUIRED) ENDIF(NOT HAMLIB_FIND_QUIETLY) ENDIF(NOT HAMLIB_FOUND)CubicSDR-0.2.3/cmake/cmake_uninstall.cmake.in000066400000000000000000000017251322677621400207710ustar00rootroot00000000000000IF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: '${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt'") ENDIF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") FILE(READ "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt" files) STRING(REGEX REPLACE " " ";" files "${files}") STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling '$ENV{DESTDIR}${file}'") IF(EXISTS "$ENV{DESTDIR}${file}") EXEC_PROGRAM( "${CMAKE_COMMAND}" ARGS "-E remove '$ENV{DESTDIR}${file}'" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF(NOT "${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing '$ENV{DESTDIR}${file}'") ENDIF(NOT "${rm_retval}" STREQUAL 0) ELSE(EXISTS "$ENV{DESTDIR}${file}") MESSAGE(STATUS "File '$ENV{DESTDIR}${file}' does not exist.") ENDIF(EXISTS "$ENV{DESTDIR}${file}") ENDFOREACH(file)CubicSDR-0.2.3/cmake/code_sign.sh.in000077500000000000000000000005351322677621400171050ustar00rootroot00000000000000#!/bin/bash for f in ${APPS}/Contents/MacOS/*.dylib do /usr/bin/codesign --force --verify --verbose --sign "${CUBICSDR_CERT}" $f done for f in ${APPS}/Contents/MacOS/modules/*.so do /usr/bin/codesign --force --verify --verbose --sign "${CUBICSDR_CERT}" $f done /usr/bin/codesign --force --verify --verbose --sign "${CUBICSDR_CERT}" ${APPS} CubicSDR-0.2.3/cmake/dmg_sign.sh.in000077500000000000000000000002001322677621400167270ustar00rootroot00000000000000#!/bin/bash /usr/bin/codesign --force --verify --verbose --sign "${CUBICSDR_CERT}" CubicSDR-${CPACK_PACKAGE_VERSION}-Darwin.dmg CubicSDR-0.2.3/cubicsdr.rc000066400000000000000000000000771322677621400152540ustar00rootroot00000000000000id ICON "icon/CubicSDR.ico" frame_icon ICON "icon/CubicSDR.ico"CubicSDR-0.2.3/external/000077500000000000000000000000001322677621400147465ustar00rootroot00000000000000CubicSDR-0.2.3/external/cubicvr2/000077500000000000000000000000001322677621400164655ustar00rootroot00000000000000CubicSDR-0.2.3/external/cubicvr2/math/000077500000000000000000000000001322677621400174165ustar00rootroot00000000000000CubicSDR-0.2.3/external/cubicvr2/math/aabb.h000066400000000000000000000071111322677621400204540ustar00rootroot00000000000000// // aabb.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__aabb__ #define __CubicVR2__aabb__ #include #include "vec3.h" namespace CubicVR { enum aabb_enum { AABB_DISJOINT, AABB_A_INSIDE_B, AABB_B_INSIDE_A, AABB_INTERSECT }; struct aabb { vec3 min, max; aabb(vec3 min_in, vec3 max_in) { min=min_in; max=max_in; } aabb() { min=max=vec3(0,0,0); } aabb engulf(aabb aabb, vec3 point) { if (aabb.min[0] > point[0]) { aabb.min[0] = point[0]; } if (aabb.min[1] > point[1]) { aabb.min[1] = point[1]; } if (aabb.min[2] > point[2]) { aabb.min[2] = point[2]; } if (aabb.max[0] < point[0]) { aabb.max[0] = point[0]; } if (aabb.max[1] < point[1]) { aabb.max[1] = point[1]; } if (aabb.max[2] < point[2]) { aabb.max[2] = point[2]; } return aabb; }; static aabb reset(aabb aabb, vec3 point=vec3(0.0f,0.0f,0.0f)) { aabb.min[0] = point[0]; aabb.min[1] = point[1]; aabb.min[2] = point[2]; aabb.max[0] = point[0]; aabb.max[1] = point[1]; aabb.max[2] = point[2]; return aabb; }; static vec3 size(aabb aabb) { __float x = aabb.min[0] < aabb.max[0] ? aabb.max[0] - aabb.min[0] : aabb.min[0] - aabb.max[0]; __float y = aabb.min[1] < aabb.max[1] ? aabb.max[1] - aabb.min[1] : aabb.min[1] - aabb.max[1]; __float z = aabb.min[2] < aabb.max[2] ? aabb.max[2] - aabb.min[2] : aabb.min[2] - aabb.max[2]; return vec3(x,y,z); }; /** Returns positive integer if intersect between A and B, 0 otherwise. For more detailed intersect result check value: CubicVR.enums.aabb.INTERSECT if AABBs intersect CubicVR.enums.aabb.A_INSIDE_B if boxA is inside boxB CubicVR.enums.aabb.B_INSIDE_A if boxB is inside boxA CubicVR.enums.aabb.DISJOINT if AABBs are disjoint (do not intersect) */ aabb_enum intersects(aabb boxA, aabb boxB) { // Disjoint if( boxA.min[0] > boxB.max[0] || boxA.max[0] < boxB.min[0] ){ return AABB_DISJOINT; } if( boxA.min[1] > boxB.max[1] || boxA.max[1] < boxB.min[1] ){ return AABB_DISJOINT; } if( boxA.min[2] > boxB.max[2] || boxA.max[2] < boxB.min[2] ){ return AABB_DISJOINT; } // boxA is inside boxB. if( boxA.min[0] >= boxB.min[0] && boxA.max[0] <= boxB.max[0] && boxA.min[1] >= boxB.min[1] && boxA.max[1] <= boxB.max[1] && boxA.min[2] >= boxB.min[2] && boxA.max[2] <= boxB.max[2]) { return AABB_A_INSIDE_B; } // boxB is inside boxA. if( boxB.min[0] >= boxA.min[0] && boxB.max[0] <= boxA.max[0] && boxB.min[1] >= boxA.min[1] && boxB.max[1] <= boxA.max[1] && boxB.min[2] >= boxA.min[2] && boxB.max[2] <= boxA.max[2]) { return AABB_B_INSIDE_A; } // Otherwise AABB's intersect. return AABB_INTERSECT; } }; }; #endif /* defined(__CubicVR2__aabb__) */ CubicSDR-0.2.3/external/cubicvr2/math/cubic_math.cpp000066400000000000000000000020251322677621400222170ustar00rootroot00000000000000// // math.cpp // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #include "cubic_math.h" namespace CubicVR { std::ostream& operator<<(std::ostream &strm, const vec4 &v) { return strm << "{" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << "}"; } std::ostream& operator<<(std::ostream &strm, const vec3 &v) { return strm << "{" << v.x << ", " << v.y << ", " << v.z << "}"; } std::ostream& operator<<(std::ostream &strm, const vec2 &v) { return strm << "{" << v.x << ", " << v.y << "}"; } std::ostream& operator<<(std::ostream &strm, const mat4 &m) { return strm << "{ " << m[0] << ", " << m[1] << ", " << m[2] << ", " << m[3] << endl << " " << m[4] << ", " << m[5] << ", " << m[6] << ", " << m[7] << endl << " " << m[8] << ", " << m[9] << ", " << m[10] << ", " << m[11] << endl << " " << m[12] << ", " << m[13] << ", " << m[14] << ", " << m[15] << " }" << endl; } } CubicSDR-0.2.3/external/cubicvr2/math/cubic_math.h000066400000000000000000000014201322677621400216620ustar00rootroot00000000000000// // math.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__math__ #define __CubicVR2__math__ #include #include "aabb.h" #include "mat3.h" #include "mat4.h" #include "quaternion.h" #include "transform.h" #include "triangle.h" #include "vec2.h" #include "vec3.h" #include "vec4.h" #include "plane.h" #include "sphere.h" #include "frustum.h" namespace CubicVR { std::ostream& operator<<(std::ostream &strm, const vec4 &v); std::ostream& operator<<(std::ostream &strm, const vec3 &v); std::ostream& operator<<(std::ostream &strm, const vec2 &v); std::ostream& operator<<(std::ostream &strm, const mat4 &m); } #endif /* defined(__CubicVR2__math__) */ CubicSDR-0.2.3/external/cubicvr2/math/cubic_types.h000066400000000000000000000024551322677621400221060ustar00rootroot00000000000000// // types.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-21. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef CubicVR2_types_h #define CubicVR2_types_h namespace CubicVR { typedef double __float64; typedef float __float32; typedef __float32 __float; #define COMBINE(x,y) x ## y #define floatSG(c, x,y) \ __float COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(__float value) { y = value; return *this; } #define intSG(c, x,y) \ int COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(int value) { y = value; return *this; } #define uintSG(c, x,y) \ unsigned int COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(unsigned int value) { y = value; return *this; } #define boolSG(c,x,y) \ bool COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(bool value) { y = value; return *this; } #define stringSG(c,x,y) \ string COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(string value) { y = value; return *this; } #define isBoolSG(c,x,y) \ bool COMBINE(is,x)() { return y; } \ c & COMBINE(set,x)(bool value) { y = value; return *this; } } #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #endif CubicSDR-0.2.3/external/cubicvr2/math/frustum.h000066400000000000000000000140321322677621400212740ustar00rootroot00000000000000// // frustum.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef CubicVR2_frustum_h #define CubicVR2_frustum_h #include #include "cubic_types.h" #include "mat4.h" #include "vec3.h" #include "vec4.h" #include "plane.h" namespace CubicVR { enum frustum_plane { PLANE_LEFT, PLANE_RIGHT, PLANE_TOP, PLANE_BOTTOM, PLANE_NEAR, PLANE_FAR }; struct frustum { std::vector planes; vec4 sphere; frustum() { planes.resize(6); for (int i = 0; i < 6; ++i) { planes[i] = vec4(0, 0, 0, 0); } //for } //Frustum::Constructor void extract(vec3 position, mat4 mvMatrix, mat4 pMatrix) { mat4 comboMatrix = mat4::multiply(pMatrix, mvMatrix, true); // Left clipping plane planes[PLANE_LEFT][0] = comboMatrix[3] + comboMatrix[0]; planes[PLANE_LEFT][1] = comboMatrix[7] + comboMatrix[4]; planes[PLANE_LEFT][2] = comboMatrix[11] + comboMatrix[8]; planes[PLANE_LEFT][3] = comboMatrix[15] + comboMatrix[12]; // Right clipping plane planes[PLANE_RIGHT][0] = comboMatrix[3] - comboMatrix[0]; planes[PLANE_RIGHT][1] = comboMatrix[7] - comboMatrix[4]; planes[PLANE_RIGHT][2] = comboMatrix[11] - comboMatrix[8]; planes[PLANE_RIGHT][3] = comboMatrix[15] - comboMatrix[12]; // Top clipping plane planes[PLANE_TOP][0] = comboMatrix[3] - comboMatrix[1]; planes[PLANE_TOP][1] = comboMatrix[7] - comboMatrix[5]; planes[PLANE_TOP][2] = comboMatrix[11] - comboMatrix[9]; planes[PLANE_TOP][3] = comboMatrix[15] - comboMatrix[13]; // Bottom clipping plane planes[PLANE_BOTTOM][0] = comboMatrix[3] + comboMatrix[1]; planes[PLANE_BOTTOM][1] = comboMatrix[7] + comboMatrix[5]; planes[PLANE_BOTTOM][2] = comboMatrix[11] + comboMatrix[9]; planes[PLANE_BOTTOM][3] = comboMatrix[15] + comboMatrix[13]; // Near clipping plane planes[PLANE_NEAR][0] = comboMatrix[3] + comboMatrix[2]; planes[PLANE_NEAR][1] = comboMatrix[7] + comboMatrix[6]; planes[PLANE_NEAR][2] = comboMatrix[11] + comboMatrix[10]; planes[PLANE_NEAR][3] = comboMatrix[15] + comboMatrix[14]; // Far clipping plane planes[PLANE_FAR][0] = comboMatrix[3] - comboMatrix[2]; planes[PLANE_FAR][1] = comboMatrix[7] - comboMatrix[6]; planes[PLANE_FAR][2] = comboMatrix[11] - comboMatrix[10]; planes[PLANE_FAR][3] = comboMatrix[15] - comboMatrix[14]; for (unsigned int i = 0; i < 6; ++i) { planes[i] = vec4::normalize(planes[i]); } //Sphere __float fov = 1 / pMatrix[5]; __float znear = -planes[PLANE_NEAR][3]; __float zfar = planes[PLANE_FAR][3]; __float view_length = zfar - znear; __float height = view_length * fov; __float width = height; vec3 P(0, 0, znear + view_length * 0.5f); vec3 Q(width, height, znear + view_length); vec3 diff = vec3::subtract(P, Q); __float diff_mag = vec3::length(diff); vec3 look_v = vec3(comboMatrix[3], comboMatrix[9], comboMatrix[10]); __float look_mag = vec3::length(look_v); look_v = vec3::multiply(look_v, 1 / look_mag); vec3 pos = vec3(position[0], position[1], position[2]); pos = vec3::add(pos, vec3::multiply(look_v, view_length * 0.5f)); pos = vec3::add(pos, vec3::multiply(look_v, 1)); sphere = vec4(pos[0], pos[1], pos[2], diff_mag); }; //Frustum::extract int contains_sphere(vec4 sphere) { for (unsigned int i = 0; i < 6; ++i) { vec4 &p = planes[i]; vec3 normal = vec3(p[0], p[1], p[2]); __float distance = vec3::dot(normal, vec3(sphere[0],sphere[1],sphere[2])) + p[3]; //OUT if (distance < -sphere[3]) { return -1; } //INTERSECT if (fabs(distance) < sphere[3]) { return 0; } } //for //IN return 1; }; //Frustum::contains_sphere int contains_box(aabb bbox) { int total_in = 0; vec3 points[8]; points[0] = bbox.min; points[1] = vec3(bbox.min[0], bbox.min[1], bbox.max[2]); points[2] = vec3(bbox.min[0], bbox.max[1], bbox.min[2]); points[3] = vec3(bbox.min[0], bbox.max[1], bbox.max[2]); points[4] = vec3(bbox.max[0], bbox.min[1], bbox.min[2]); points[5] = vec3(bbox.max[0], bbox.min[1], bbox.max[2]); points[6] = vec3(bbox.max[0], bbox.max[1], bbox.min[2]); points[7] = bbox.max; for (unsigned int i = 0; i < 6; ++i) { unsigned int in_count = 8; unsigned int point_in = 1; for (unsigned int j = 0; j < 8; ++j) { if (plane::classifyPoint(planes[i], points[j]) == -1) { point_in = 0; --in_count; } //if } //for j //OUT if (in_count == 0) { return -1; } total_in += point_in; } //for i //IN if (total_in == 6) { return 1; } return 0; }; //Frustum::contains_box }; } #endif CubicSDR-0.2.3/external/cubicvr2/math/mat3.h000066400000000000000000000050201322677621400204300ustar00rootroot00000000000000// // mat3.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__mat3__ #define __CubicVR2__mat3__ #include #include "vec3.h" #include #include namespace CubicVR { #define mat3SG(c,x,y) \ mat3 COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(mat3 value) { y = value; return *this; } struct mat3 { __float a,b,c,d,e,f,g,h,i; //access as-array: inline __float& operator [] (size_t i) { __float* as_array = (__float*)this; return (as_array[i]); } inline const __float& operator [] (size_t i) const { __float* as_array = (__float*)this; return (as_array[i]); } mat3(__float ai,__float bi,__float ci,__float di,__float ei,__float fi,__float gi,__float hi,__float ii) { a = ai; b = bi; c = ci; d = di; e = ei; f = fi; g = gi; h = hi; i = ii; }; mat3() { memset((__float *)this, 0, sizeof(mat3)); } // mat3 operator* (mat4 m) { return mat3::multiply(*this,m); }; // void operator*= (mat4 m) { *this = mat3::multiply(*this,m); }; static mat3 identity() { return mat3(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); } static void transpose_inline(mat3 &mat) { __float a01 = mat[1], a02 = mat[2], a12 = mat[5]; mat[1] = mat[3]; mat[2] = mat[6]; mat[3] = a01; mat[5] = mat[7]; mat[6] = a02; mat[7] = a12; }; static mat3 transpose(mat3 mat_in) { __float a01 = mat_in[1], a02 = mat_in[2], a12 = mat_in[5]; mat3 mat; mat[1] = mat_in[3]; mat[2] = mat_in[6]; mat[3] = a01; mat[5] = mat_in[7]; mat[6] = a02; mat[7] = a12; return mat; }; static vec3 multiply(mat3 m1, vec3 m2) { vec3 mOut; mOut[0] = m2[0] * m1[0] + m2[3] * m1[1] + m2[6] * m1[2] ; mOut[1] = m2[1] * m1[0] + m2[4] * m1[1] + m2[7] * m1[2] ; mOut[2] = m2[2] * m1[0] + m2[5] * m1[1] + m2[8] * m1[2]; return mOut; }; }; } #endif /* defined(__CubicVR2__mat3__) */ CubicSDR-0.2.3/external/cubicvr2/math/mat4.h000066400000000000000000000357621322677621400204510ustar00rootroot00000000000000// // mat4.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-21. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__mat4__ #define __CubicVR2__mat4__ #include #include "cubic_types.h" #include "vec3.h" #include "vec4.h" #include "mat3.h" #include #include namespace CubicVR { using namespace std; #define mat4SG(c,x,y) \ mat4 COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(mat4 value) { y = value; return *this; } struct mat4 { //16 elements __float a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p; //access as-array: inline __float& operator [] (size_t i) { __float* as_array = (__float*)this; return (as_array[i]); } inline const __float& operator [] (size_t i) const { __float* as_array = (__float*)this; return (as_array[i]); } //To be accessed by GL API directly, accessed by pointer. //operator* overloading is way too dangerous, especially in ptr != NULL //tests. inline __float* to_ptr() const { return (__float *)this; } mat4(__float ai,__float bi,__float ci,__float di,__float ei,__float fi,__float gi,__float hi,__float ii,__float ji,__float ki,__float li,__float mi,__float ni,__float oi,__float pi) { a = ai; b = bi; c = ci; d = di; e = ei; f = fi; g = gi; h = hi; i = ii; j = ji; k = ki; l = li; m = mi; n = ni; o = oi; p = pi; } mat4() { memset(this,0,sizeof(mat4)); } mat4 operator* (mat4 m) { return mat4::multiply(*this, m, true); }; void operator*= (mat4 m) { *this = mat4::multiply(*this, m, true); }; // mat4 &operator= (const mat4 &m) { memcpy(this,(__float *)m,sizeof(__float)*16); return *this; }; static mat4 identity() { return mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); } static mat4 multiply(mat4 mLeft, mat4 mRight, bool /* updated */) { mat4 mOut; mOut[0] = mLeft[0] * mRight[0] + mLeft[4] * mRight[1] + mLeft[8] * mRight[2] + mLeft[12] * mRight[3]; mOut[1] = mLeft[1] * mRight[0] + mLeft[5] * mRight[1] + mLeft[9] * mRight[2] + mLeft[13] * mRight[3]; mOut[2] = mLeft[2] * mRight[0] + mLeft[6] * mRight[1] + mLeft[10] * mRight[2] + mLeft[14] * mRight[3]; mOut[3] = mLeft[3] * mRight[0] + mLeft[7] * mRight[1] + mLeft[11] * mRight[2] + mLeft[15] * mRight[3]; mOut[4] = mLeft[0] * mRight[4] + mLeft[4] * mRight[5] + mLeft[8] * mRight[6] + mLeft[12] * mRight[7]; mOut[5] = mLeft[1] * mRight[4] + mLeft[5] * mRight[5] + mLeft[9] * mRight[6] + mLeft[13] * mRight[7]; mOut[6] = mLeft[2] * mRight[4] + mLeft[6] * mRight[5] + mLeft[10] * mRight[6] + mLeft[14] * mRight[7]; mOut[7] = mLeft[3] * mRight[4] + mLeft[7] * mRight[5] + mLeft[11] * mRight[6] + mLeft[15] * mRight[7]; mOut[8] = mLeft[0] * mRight[8] + mLeft[4] * mRight[9] + mLeft[8] * mRight[10] + mLeft[12] * mRight[11]; mOut[9] = mLeft[1] * mRight[8] + mLeft[5] * mRight[9] + mLeft[9] * mRight[10] + mLeft[13] * mRight[11]; mOut[10] = mLeft[2] * mRight[8] + mLeft[6] * mRight[9] + mLeft[10] * mRight[10] + mLeft[14] * mRight[11]; mOut[11] = mLeft[3] * mRight[8] + mLeft[7] * mRight[9] + mLeft[11] * mRight[10] + mLeft[15] * mRight[11]; mOut[12] = mLeft[0] * mRight[12] + mLeft[4] * mRight[13] + mLeft[8] * mRight[14] + mLeft[12] * mRight[15]; mOut[13] = mLeft[1] * mRight[12] + mLeft[5] * mRight[13] + mLeft[9] * mRight[14] + mLeft[13] * mRight[15]; mOut[14] = mLeft[2] * mRight[12] + mLeft[6] * mRight[13] + mLeft[10] * mRight[14] + mLeft[14] * mRight[15]; mOut[15] = mLeft[3] * mRight[12] + mLeft[7] * mRight[13] + mLeft[11] * mRight[14] + mLeft[15] * mRight[15]; return mOut; }; static vec3 multiply(mat4 m1, vec3 m2, bool /* updated */) { vec3 mOut; mOut[0] = m1[0] * m2[0] + m1[4] * m2[1] + m1[8] * m2[2] + m1[12]; mOut[1] = m1[1] * m2[0] + m1[5] * m2[1] + m1[9] * m2[2] + m1[13]; mOut[2] = m1[2] * m2[0] + m1[6] * m2[1] + m1[10] * m2[2] + m1[14]; return mOut; } static mat4 frustum(__float left, __float right, __float bottom, __float top, __float zNear, __float zFar) { __float A = (right + left) / (right - left); __float B = (top + bottom) / (top - bottom); __float C = - (zFar + zNear) / (zFar - zNear); __float D = - (-2.0f * zFar * zNear) / (zFar - zNear); return mat4((2.0f * zNear) / (right - left), 0, A, 0, 0, (2.0f * zNear) / (top - bottom), B, 0, 0, 0, C, D, 0, 0, -1, 0); }; static mat4 perspective(__float fovy, __float aspect, __float zNear, __float zFar) { __float yFac = tan(fovy * (float)M_PI / 360.0f); __float xFac = yFac * aspect; return mat4::frustum(-xFac, xFac, -yFac, yFac, zNear, zFar); }; static mat4 ortho(__float left,__float right,__float bottom,__float top,__float znear,__float zfar) { return mat4(2.0f / (right - left), 0, 0, 0, 0, 2.0f / (top - bottom), 0, 0, 0, 0, -2.0f / (zfar - znear), 0, -(left + right) / (right - left), -(top + bottom) / (top - bottom), -(zfar + znear) / (zfar - znear), 1); }; static __float determinant(mat4 m) { __float a0 = m[0] * m[5] - m[1] * m[4]; __float a1 = m[0] * m[6] - m[2] * m[4]; __float a2 = m[0] * m[7] - m[3] * m[4]; __float a3 = m[1] * m[6] - m[2] * m[5]; __float a4 = m[1] * m[7] - m[3] * m[5]; __float a5 = m[2] * m[7] - m[3] * m[6]; __float b0 = m[8] * m[13] - m[9] * m[12]; __float b1 = m[8] * m[14] - m[10] * m[12]; __float b2 = m[8] * m[15] - m[11] * m[12]; __float b3 = m[9] * m[14] - m[10] * m[13]; __float b4 = m[9] * m[15] - m[11] * m[13]; __float b5 = m[10] * m[15] - m[11] * m[14]; __float det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; return det; }; // coFactor: function (m, n, out) { // // .. todo.. // }, static mat4 transpose(mat4 m) { return mat4(m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]); }; static mat3 inverse_mat3(mat4 mat) { mat3 dest; __float a00 = mat[0], a01 = mat[1], a02 = mat[2], a10 = mat[4], a11 = mat[5], a12 = mat[6], a20 = mat[8], a21 = mat[9], a22 = mat[10]; __float b01 = a22*a11-a12*a21, b11 = -a22*a10+a12*a20, b21 = a21*a10-a11*a20; __float d = a00*b01 + a01*b11 + a02*b21; if (!d) { return dest; } __float id = 1/d; dest[0] = b01*id; dest[1] = (-a22*a01 + a02*a21)*id; dest[2] = (a12*a01 - a02*a11)*id; dest[3] = b11*id; dest[4] = (a22*a00 - a02*a20)*id; dest[5] = (-a12*a00 + a02*a10)*id; dest[6] = b21*id; dest[7] = (-a21*a00 + a01*a20)*id; dest[8] = (a11*a00 - a01*a10)*id; return dest; }; static mat4 inverse(mat4 m) { mat4 m_inv; __float a0 = m[0] * m[5] - m[1] * m[4]; __float a1 = m[0] * m[6] - m[2] * m[4]; __float a2 = m[0] * m[7] - m[3] * m[4]; __float a3 = m[1] * m[6] - m[2] * m[5]; __float a4 = m[1] * m[7] - m[3] * m[5]; __float a5 = m[2] * m[7] - m[3] * m[6]; __float b0 = m[8] * m[13] - m[9] * m[12]; __float b1 = m[8] * m[14] - m[10] * m[12]; __float b2 = m[8] * m[15] - m[11] * m[12]; __float b3 = m[9] * m[14] - m[10] * m[13]; __float b4 = m[9] * m[15] - m[11] * m[13]; __float b5 = m[10] * m[15] - m[11] * m[14]; __float determinant = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; if (determinant != 0) { m_inv[0] = 0 + m[5] * b5 - m[6] * b4 + m[7] * b3; m_inv[4] = 0 - m[4] * b5 + m[6] * b2 - m[7] * b1; m_inv[8] = 0 + m[4] * b4 - m[5] * b2 + m[7] * b0; m_inv[12] = 0 - m[4] * b3 + m[5] * b1 - m[6] * b0; m_inv[1] = 0 - m[1] * b5 + m[2] * b4 - m[3] * b3; m_inv[5] = 0 + m[0] * b5 - m[2] * b2 + m[3] * b1; m_inv[9] = 0 - m[0] * b4 + m[1] * b2 - m[3] * b0; m_inv[13] = 0 + m[0] * b3 - m[1] * b1 + m[2] * b0; m_inv[2] = 0 + m[13] * a5 - m[14] * a4 + m[15] * a3; m_inv[6] = 0 - m[12] * a5 + m[14] * a2 - m[15] * a1; m_inv[10] = 0 + m[12] * a4 - m[13] * a2 + m[15] * a0; m_inv[14] = 0 - m[12] * a3 + m[13] * a1 - m[14] * a0; m_inv[3] = 0 - m[9] * a5 + m[10] * a4 - m[11] * a3; m_inv[7] = 0 + m[8] * a5 - m[10] * a2 + m[11] * a1; m_inv[11] = 0 - m[8] * a4 + m[9] * a2 - m[11] * a0; m_inv[15] = 0 + m[8] * a3 - m[9] * a1 + m[10] * a0; __float inverse_det = 1.0f / determinant; m_inv[0] *= inverse_det; m_inv[1] *= inverse_det; m_inv[2] *= inverse_det; m_inv[3] *= inverse_det; m_inv[4] *= inverse_det; m_inv[5] *= inverse_det; m_inv[6] *= inverse_det; m_inv[7] *= inverse_det; m_inv[8] *= inverse_det; m_inv[9] *= inverse_det; m_inv[10] *= inverse_det; m_inv[11] *= inverse_det; m_inv[12] *= inverse_det; m_inv[13] *= inverse_det; m_inv[14] *= inverse_det; m_inv[15] *= inverse_det; return m_inv; } return mat4::identity(); }; static mat4 translate(__float x, __float y, __float z) { mat4 m = mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, x, y, z, 1.0f); return m; }; static mat4 rotateAxis(__float r, __float x, __float y, __float z) { // rotate r about axis x,y,z __float sAng = sinf(r*((float)M_PI/180.0f)); __float cAng = cosf(r*((float)M_PI/180.0f)); return mat4( cAng+(x*x)*(1.0f-cAng), x*y*(1.0f-cAng) - z*sAng, x*z*(1.0f-cAng) + y*sAng, 0, y*x*(1.0f-cAng)+z*sAng, cAng + y*y*(1.0f-cAng), y*z*(1.0f-cAng)-x*sAng, 0, z*x*(1.0f-cAng)-y*sAng, z*y*(1.0f-cAng)+x*sAng, cAng+(z*z)*(1.0f-cAng), 0, 0, 0, 0, 1 ); }; static mat4 rotate(__float x, __float y, __float z) { // rotate each axis, angles x, y, z in turn __float sAng,cAng; mat4 mOut = mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); if (z!=0) { sAng = sinf(z*((float)M_PI/180.0f)); cAng = cosf(z*((float)M_PI/180.0f)); mOut *= mat4(cAng, sAng, 0.0f, 0.0f, -sAng, cAng, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); } if (y!=0) { sAng = sinf(y*((float)M_PI/180.0f)); cAng = cosf(y*((float)M_PI/180.0f)); mOut *= mat4(cAng, 0.0f, -sAng, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, sAng, 0.0f, cAng, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); } if (x!=0) { sAng = sinf(x*((float)M_PI/180.0f)); cAng = cosf(x*((float)M_PI/180.0f)); mOut *= mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, cAng, sAng, 0.0f, 0.0f, -sAng, cAng, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); } return mOut; }; static mat4 scale(__float x, __float y, __float z) { return mat4(x, 0.0f, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 0.0f, z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); }; static mat4 transform(vec3 position, vec3 rotation, vec3 scale) { mat4 m = mat4::identity(); m *= mat4::translate(position[0],position[1],position[2]); if (!(rotation[0] == 0 && rotation[1] == 0 && rotation[2] == 0)) { m *= mat4::rotate(rotation[0], rotation[1], rotation[2]); } if (!(scale[0] == 1 && scale[1] == 1 && scale[2] == 1)) { m *= mat4::scale(scale[0],scale[1],scale[2]); } return m; }; static vec4 vec4_multiply(vec4 m1, mat4 m2) { vec4 mOut; mOut[0] = m2[0] * m1[0] + m2[4] * m1[1] + m2[8] * m1[2] + m2[12] * m1[3]; mOut[1] = m2[1] * m1[0] + m2[5] * m1[1] + m2[9] * m1[2] + m2[13] * m1[3]; mOut[2] = m2[2] * m1[0] + m2[6] * m1[1] + m2[10] * m1[2] + m2[14] * m1[3]; mOut[3] = m2[3] * m1[0] + m2[7] * m1[1] + m2[11] * m1[2] + m2[15] * m1[3]; return mOut; }; static mat4 lookat(__float eyex, __float eyey, __float eyez, __float centerx, __float centery, __float centerz, __float upx, __float upy, __float upz) { vec3 forward, side, up; forward[0] = centerx - eyex; forward[1] = centery - eyey; forward[2] = centerz - eyez; up[0] = upx; up[1] = upy; up[2] = upz; forward = vec3::normalize(forward); /* Side = forward x up */ side = vec3::cross(forward, up); side = vec3::normalize(side); /* Recompute up as: up = side x forward */ up = vec3::cross(side, forward); return mat4::translate(-eyex,-eyey,-eyez) * mat4( side[0], up[0], -forward[0], 0, side[1], up[1], -forward[1], 0, side[2], up[2], -forward[2], 0, 0, 0, 0, 1); }; static vec3 unProject(mat4 pMatrix, mat4 mvMatrix, float width, float height, float winx, float winy, float /* winz */) { vec4 p(((winx / width) * 2.0f) - 1.0f, -(((winy / height) * 2.0f) - 1.0f), 1.0f, 1.0f); vec4 invp = mat4::vec4_multiply(mat4::vec4_multiply(p, mat4::inverse(pMatrix)), mat4::inverse(mvMatrix)); vec3 result(invp[0] / invp[3], invp[1] / invp[3], invp[2] / invp[3]); return result; }; }; } #endif /* defined(__CubicVR2__mat4__) */ CubicSDR-0.2.3/external/cubicvr2/math/plane.h000066400000000000000000000012001322677621400206570ustar00rootroot00000000000000// // plane.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef CubicVR2_plane_h #define CubicVR2_plane_h #include "vec4.h" #include "vec3.h" namespace CubicVR { struct plane : vec4 { static int classifyPoint(vec4 plane, vec3 pt) { __float dist = (plane[0] * pt[0]) + (plane[1] * pt[1]) + (plane[2] * pt[2]) + (plane[3]); if (dist < 0) { return -1; } else if (dist > 0) { return 1; } return 0; }; }; } #endif CubicSDR-0.2.3/external/cubicvr2/math/quaternion.h000066400000000000000000000073271322677621400217650ustar00rootroot00000000000000// // quaternion.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__quaternion__ #define __CubicVR2__quaternion__ #include #include "vec4.h" #include "mat4.h" namespace CubicVR { struct quaternion : vec4 { static vec4 fromMatrix(mat4 mat) { __float t = 1 + mat[0] + mat[5] + mat[10]; __float S,X,Y,Z,W; if ( t > 0.00000001 ) { S = sqrtf(t) * 2; X = ( mat[9] - mat[6] ) / S; Y = ( mat[2] - mat[8] ) / S; Z = ( mat[4] - mat[1] ) / S; W = 0.25f * S; } else { if ( mat[0] > mat[5] && mat[0] > mat[10] ) { // Column 0: S = sqrtf( 1.0f + mat[0] - mat[5] - mat[10] ) * 2.0f; X = 0.25f * S; Y = (mat[4] + mat[1] ) / S; Z = (mat[2] + mat[8] ) / S; W = (mat[9] - mat[6] ) / S; } else if ( mat[5] > mat[10] ) { // Column 1: S = sqrtf( 1.0f + mat[5] - mat[0] - mat[10] ) * 2.0f; X = (mat[4] + mat[1] ) / S; Y = 0.25f * S; Z = (mat[9] + mat[6] ) / S; W = (mat[2] - mat[8] ) / S; } else { // Column 2: S = sqrtf( 1.0f + mat[10] - mat[0] - mat[5] ) * 2.0f; X = (mat[2] + mat[8] ) / S; Y = (mat[9] + mat[6] ) / S; Z = 0.25f * S; W = (mat[4] - mat[1] ) / S; } } return vec4(X,Y,Z,W); }; static vec4 fromEuler(__float bank, __float heading, __float pitch) { // x,y,z __float c1 = cosf(((float)M_PI / 180.0f) * heading / 2.0f); __float s1 = sinf(((float)M_PI / 180.0f) * heading / 2.0f); __float c2 = cosf(((float)M_PI / 180.0f) * pitch / 2.0f); __float s2 = sinf(((float)M_PI / 180.0f) * pitch / 2.0f); __float c3 = cosf(((float)M_PI / 180.0f) * bank / 2.0f); __float s3 = sinf(((float)M_PI / 180.0f) * bank / 2.0f); __float c1c2 = c1 * c2; __float s1s2 = s1 * s2; vec4 mOut; mOut[0] = c1c2 * c3 - s1s2 * s3; mOut[1] = c1c2 * s3 + s1s2 * c3; mOut[2] = s1 * c2 * c3 + c1 * s2 * s3; mOut[3] = c1 * s2 * c3 - s1 * c2 * s3; return mOut; }; static vec3 toEuler(vec4 q) { __float sqx = q[0] * q[0]; __float sqy = q[1] * q[1]; __float sqz = q[2] * q[2]; __float sqw = q[3] * q[3]; __float x = (180.0f / (float)M_PI) * ((atan2f(2.0f * (q[1] * q[2] + q[0] * q[3]), (-sqx - sqy + sqz + sqw)))); __float y = (180.0f / (float)M_PI) * ((asinf(-2.0f * (q[0] * q[2] - q[1] * q[3])))); __float z = (180.0f / (float)M_PI) * ((atan2f(2.0f * (q[0] * q[1] + q[2] * q[3]), (sqx - sqy - sqz + sqw)))); return vec3(x, y, z); }; static vec4 multiply(vec4 q1, vec4 q2) { __float x = q1[0] * q2[3] + q1[3] * q2[0] + q1[1] * q2[2] - q1[2] * q2[1]; __float y = q1[1] * q2[3] + q1[3] * q2[1] + q1[2] * q2[0] - q1[0] * q2[2]; __float z = q1[2] * q2[3] + q1[3] * q2[2] + q1[0] * q2[1] - q1[1] * q2[0]; __float w = q1[3] * q2[3] - q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2]; return vec4(x,y,z,w); }; }; } #endif /* defined(__CubicVR2__quaternion__) */ CubicSDR-0.2.3/external/cubicvr2/math/sphere.h000066400000000000000000000015011322677621400210520ustar00rootroot00000000000000// // sphere.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef CubicVR2_sphere_h #define CubicVR2_sphere_h #include "vec3.h" #include "vec4.h" namespace CubicVR { struct sphere { bool intersects(vec4 sphere, vec4 other) { vec3 spherePos(sphere[0], sphere[1], sphere[2]); vec3 otherPos(other[0], other[1], other[2]); vec3 diff = vec3::subtract(spherePos, otherPos); __float mag = sqrtf(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]); __float sum_radii = sphere[3] + other[3]; if (mag * mag < sum_radii * sum_radii) { return true; } return false; } }; } #endif CubicSDR-0.2.3/external/cubicvr2/math/transform.h000066400000000000000000000107231322677621400216050ustar00rootroot00000000000000// // Transform.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__Transform__ #define __CubicVR2__Transform__ #include #include "cubic_types.h" #include "mat4.h" #include "vec3.h" #include namespace CubicVR { class transform { std::vector m_stack; std::vector m_cache; int c_stack; int valid; mat4 result; transform() { c_stack = 0; valid = false; result = mat4::identity(); }; transform(mat4 init_mat) { clearStack(init_mat); }; void setIdentity() { m_stack[c_stack] = mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); if (valid == c_stack && c_stack) { valid--; } } mat4 getIdentity() { return mat4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); } void invalidate() { valid = 0; result = mat4::identity(); } mat4 getResult() { if (!c_stack) { return m_stack[0]; } mat4 m = getIdentity(); if (valid > c_stack-1) valid = c_stack-1; for (int i = valid; i < c_stack+1; i++) { m = mat4::multiply(m, m_stack[i], true); m_cache[i] = m; } valid = c_stack-1; result = m_cache[c_stack]; return result; } void pushMatrix(mat4 m) { c_stack++; m_stack[c_stack] = m; } void popMatrix() { if (c_stack == 0) { return; } c_stack--; } void clearStack(mat4 init_mat) { m_stack.clear(); m_cache.clear(); c_stack = 0; valid = 0; result = mat4::identity(); m_stack[0] = init_mat; } void translate(__float x, __float y, __float z) { mat4 m = getIdentity(); m[12] = x; m[13] = y; m[14] = z; m_stack[c_stack] = mat4::multiply(m, m_stack[c_stack], true); if (valid == c_stack && c_stack) { valid--; } } void scale(__float x, __float y, __float z) { mat4 m = getIdentity(); m[0] = x; m[5] = y; m[10] = z; m_stack[c_stack] = mat4::multiply(m, m_stack[c_stack], true); if (valid == c_stack && c_stack) { valid--; } } void rotate(__float ang, __float x, __float y, __float z) { __float sAng, cAng; if (x || y || z) { sAng = sin(-ang * ((float)M_PI / 180.0f)); cAng = cos(-ang * ((float)M_PI / 180.0f)); } if (x) { mat4 X_ROT = getIdentity(); X_ROT[5] = cAng * x; X_ROT[9] = sAng * x; X_ROT[6] = -sAng * x; X_ROT[10] = cAng * x; m_stack[c_stack] = mat4::multiply(m_stack[c_stack], X_ROT, true); } if (y) { mat4 Y_ROT = getIdentity(); Y_ROT[0] = cAng * y; Y_ROT[8] = -sAng * y; Y_ROT[2] = sAng * y; Y_ROT[10] = cAng * y; m_stack[c_stack] = mat4::multiply(m_stack[c_stack], Y_ROT, true); } if (z) { mat4 Z_ROT = getIdentity(); Z_ROT[0] = cAng * z; Z_ROT[4] = sAng * z; Z_ROT[1] = -sAng * z; Z_ROT[5] = cAng * z; m_stack[c_stack] = mat4::multiply(m_stack[c_stack], Z_ROT, true); } if (valid == c_stack && c_stack) { valid--; } }; }; } #endif /* defined(__CubicVR2__Transform__) */ CubicSDR-0.2.3/external/cubicvr2/math/triangle.h000066400000000000000000000016321322677621400213760ustar00rootroot00000000000000// // triangle.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__triangle__ #define __CubicVR2__triangle__ #include #include "vec3.h" namespace CubicVR { struct triangle { static vec3 normal(vec3 pt1, vec3 pt2, vec3 pt3) { __float v10 = pt1[0] - pt2[0]; __float v11 = pt1[1] - pt2[1]; __float v12 = pt1[2] - pt2[2]; __float v20 = pt2[0] - pt3[0]; __float v21 = pt2[1] - pt3[1]; __float v22 = pt2[2] - pt3[2]; vec3 mOut; mOut[0] = v11 * v22 - v12 * v21; mOut[1] = v12 * v20 - v10 * v22; mOut[2] = v10 * v21 - v11 * v20; return mOut; }; }; } #endif /* defined(__CubicVR2__triangle__) */ CubicSDR-0.2.3/external/cubicvr2/math/vec2.h000066400000000000000000000060061322677621400204300ustar00rootroot00000000000000// // vec2.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__vec2__ #define __CubicVR2__vec2__ #include #include #include "cubic_types.h" #include namespace CubicVR { #define vec2SG(c,x,y) \ vec2 COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(vec2 value) { y = value; return *this; } struct vec2 { __float x, y; public: __float& u() { return x; } __float& v() { return y; } //access as-array: inline __float& operator [] (size_t i) { __float* as_array = (__float*)this; return (as_array[i]); } inline const __float& operator [] (size_t i) const { __float* as_array = (__float*)this; return (as_array[i]); } vec2 (__float xi,__float yi) { x = xi; y = yi; } vec2 () { x = y = 0.0f; } vec2 operator*(__float v) { return vec2( x*v, y*v ); } // vec2 operator*(vec2 v) { return vec2::cross(*this,v); } vec2 operator+(vec2 v) { return vec2::add(*this,v); } vec2 operator-(vec2 v) { return vec2::subtract(*this,v); } static bool equal(vec2 a, vec2 b, __float epsilon = 0.00000001) { return (fabs(a[0] - b[0]) < epsilon && fabs(a[1] - b[1]) < epsilon); }; static bool onLine(vec2 a, vec2 b,vec2 c) { __float minx = (a[0]b[0])?a[0]:b[0]; __float maxy = (a[1]>b[1])?a[1]:b[1]; if ((minx <= c[0] && c[0] <= maxx) && (miny <= c[1] && c[1] <= maxy)) { return true; } else { return false; } }; static vec2 lineIntersect(vec2 a1, vec2 a2, vec2 b1, vec2 b2) { __float x1 = a1[0], y1 = a1[1], x2 = a2[0], y2 = a2[1]; __float x3 = b1[0], y3 = b1[1], x4 = b2[0], y4 = b2[1]; __float d = ((x1-x2) * (y3-y4)) - ((y1-y2) * (x3-x4)); if (d == 0) return vec2(INFINITY,INFINITY); __float xi = (((x3-x4) * ((x1*y2)-(y1*x2))) - ((x1-x2) *((x3*y4)-(y3*x4))))/d; __float yi = (((y3-y4) * ((x1*y2)-(y1*x2))) - ((y1-y2) *((x3*y4)-(y3*x4))))/d; return vec2( xi,yi ); }; static vec2 add(vec2 a,vec2 b) { return vec2(a[0]+b[0],a[1]+b[1]); }; static vec2 subtract(vec2 a, vec2 b) { return vec2(a[0]-b[0],a[1]-b[1]); }; static __float length(vec2 a,vec2 b) { vec2 s(a[0]-b[0],a[1]-b[1]); return sqrtf(s[0]*s[0]+s[1]*s[1]); }; static __float length(vec2 a) { return sqrtf(a[0]*a[0]+a[1]*a[1]); }; }; } #endif /* defined(__CubicVR2__vec2__) */ CubicSDR-0.2.3/external/cubicvr2/math/vec3.h000066400000000000000000000155331322677621400204360ustar00rootroot00000000000000// // vec3.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-21. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__vec3__ #define __CubicVR2__vec3__ #include #include "cubic_types.h" #include #include namespace CubicVR { #define vec3SG(c,x,y) \ vec3 COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(vec3 value) { y = value; return *this; } struct vec3 { __float x,y,z; __float& r() { return x; } __float& g() { return y; } __float& b() { return z; } //access as-array: inline __float& operator [] (size_t i) { __float* as_array = (__float*)this; return (as_array[i]); } inline const __float& operator [] (size_t i) const { __float* as_array = (__float*)this; return (as_array[i]); } vec3 (__float xi,__float yi,__float zi) { x = xi; y = yi; z = zi; } vec3 () { x = y = z = 0.0f; } vec3 operator*(__float v) { return vec3(x*v, y*v, z*v); } vec3 operator*(vec3 v) { return vec3::cross(*this,v); } vec3 operator+(vec3 v) { return vec3::add(*this,v); } vec3 operator-(vec3 v) { return vec3::subtract(*this,v); } static __float length(vec3 pta, vec3 ptb) { __float a,b,c; a = ptb[0]-pta[0]; b = ptb[1]-pta[1]; c = ptb[2]-pta[2]; return sqrtf((a*a) + (b*b) + (c*c)); }; static __float length(vec3 pta) { __float a,b,c; a = pta[0]; b = pta[1]; c = pta[2]; return sqrtf((a*a) + (b*b) + (c*c)); }; static vec3 normalize(vec3 pt) { __float a = pt[0], b = pt[1], c = pt[2], d = sqrtf((a*a) + (b*b) + (c*c)); if (d) { pt[0] = pt[0]/d; pt[1] = pt[1]/d; pt[2] = pt[2]/d; return pt; } pt = vec3(0.0f,0.0f,0.0f); return pt; }; static __float dot(vec3 v1, vec3 v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; }; static __float angle(vec3 v1, vec3 v2) { __float a = acosf((v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / (sqrtf(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) * sqrtf(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]))); return a; }; static vec3 cross(vec3 vectA, vec3 vectB) { return vec3( vectA[1] * vectB[2] - vectB[1] * vectA[2], vectA[2] * vectB[0] - vectB[2] * vectA[0], vectA[0] * vectB[1] - vectB[0] * vectA[1] ); }; static vec3 multiply(vec3 vectA, __float constB) { return vec3(vectA[0] * constB, vectA[1] * constB, vectA[2] * constB); }; static vec3 add(vec3 vectA, vec3 vectB) { return vec3(vectA[0] + vectB[0], vectA[1] + vectB[1], vectA[2] + vectB[2]); }; static vec3 subtract(vec3 vectA, vec3 vectB) { return vec3(vectA[0] - vectB[0], vectA[1] - vectB[1], vectA[2] - vectB[2]); }; static bool equal(vec3 a, vec3 b, __float epsilon = 0.0000001f) { return ((fabs(a[0] - b[0]) < epsilon) && (fabs(a[1] - b[1]) < epsilon) && (fabs(a[2] - b[2]) < epsilon)); }; static vec3 moveViewRelative(vec3 position, vec3 target, __float xdelta, __float zdelta) { __float ang = atan2f(zdelta, xdelta); __float cam_ang = atan2f(target[2] - position[2], target[0] - position[0]); __float mag = sqrtf(xdelta * xdelta + zdelta * zdelta); __float move_ang = cam_ang + ang + (float)M_PI/2.0f; // if (typeof(alt_source) === 'object') { // return [alt_source[0] + mag * Math.cos(move_ang), alt_source[1], alt_source[2] + mag * Math.sin(move_ang)]; // } return vec3(position[0] + mag * cosf(move_ang), position[1], position[2] + mag * sinf(move_ang)); }; static vec3 trackTarget(vec3 position, vec3 target, __float trackingSpeed, __float safeDistance) { vec3 camv = vec3::subtract(target, position); vec3 dist = camv; __float fdist = vec3::length(dist); vec3 motionv = camv; motionv = vec3::normalize(motionv); motionv = vec3::multiply(motionv, trackingSpeed * (1.0f / (1.0f / (fdist - safeDistance)))); vec3 ret_pos; if (fdist > safeDistance) { ret_pos = vec3::add(position, motionv); } else if (fdist < safeDistance) { motionv = camv; motionv = vec3::normalize(motionv); motionv = vec3::multiply(motionv, trackingSpeed * (1.0f / (1.0f / (fabsf(fdist - safeDistance))))); ret_pos = vec3::subtract(position, motionv); } else { ret_pos = vec3(position[0], position[1] + motionv[2], position[2]); } return ret_pos; }; static vec3 getClosestTo(vec3 ptA, vec3 ptB, vec3 ptTest) { vec3 S, T, U; S = vec3::subtract(ptB, ptA); T = vec3::subtract(ptTest, ptA); U = vec3::add(vec3::multiply(S, vec3::dot(S, T) / vec3::dot(S, S)), ptA); return U; }; // linePlaneIntersect: function(normal, point_on_plane, segment_start, segment_end) // { // // form a plane from normal and point_on_plane and test segment start->end to find intersect point // var denom,mu; // // var d = - normal[0] * point_on_plane[0] - normal[1] * point_on_plane[1] - normal[2] * point_on_plane[2]; // // // calculate position where the plane intersects the segment // denom = normal[0] * (segment_end[0] - segment_start[0]) + normal[1] * (segment_end[1] - segment_start[1]) + normal[2] * (segment_end[2] - segment_start[2]); // if (Math.fabs(denom) < 0.001) return false; // // mu = - (d + normal[0] * segment_start[0] + normal[1] * segment_start[1] + normal[2] * segment_start[2]) / denom; // return [ // (segment_start[0] + mu * (segment_end[0] - segment_start[0])), // (segment_start[1] + mu * (segment_end[1] - segment_start[1])), // (segment_start[2] + mu * (segment_end[2] - segment_start[2])) // ]; // } }; } #endif /* defined(__CubicVR2__vec3__) */ CubicSDR-0.2.3/external/cubicvr2/math/vec4.h000066400000000000000000000042121322677621400204270ustar00rootroot00000000000000// // vec4.h // CubicVR2 // // Created by Charles J. Cliffe on 2013-02-22. // Copyright (c) 2013 Charles J. Cliffe. All rights reserved. // #ifndef __CubicVR2__vec4__ #define __CubicVR2__vec4__ #include #include "cubic_types.h" #include #include namespace CubicVR { #define vec4SG(c,x,y) \ vec3 COMBINE(get,x)() { return y; } \ c & COMBINE(set,x)(vec3 value) { y = value; return *this; } struct vec4 { __float x, y, z, w; public: __float& r() { return x; } __float& g() { return y; } __float& b() { return z; } __float& a() { return w; } //access as-array: inline __float& operator [] (size_t i) { __float* as_array = (__float*)this; return (as_array[i]); } inline const __float& operator [] (size_t i) const { __float* as_array = (__float*)this; return (as_array[i]); } vec4 (__float xi,__float yi,__float zi,__float wi) { x = xi; y = yi; z = zi; w = wi; } vec4 () { x = y = z = w = 0.0f; } vec4 operator*(__float v) { return vec4(x*v, y*v, z*v, w*v); } // vec4 operator*(vec4 v) { return vec4::cross(*this,v); } // vec4 operator+(vec4 v) { return vec4::add(*this,v); } // vec4 operator-(vec4 v) { return vec4::subtract(*this,v); } static __float length(vec4 a, vec4 b) { __float v[4] = {a[0]-b[0],a[1]-b[1],a[2]-b[2],a[3]-b[3]}; return sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]); }; static __float length(vec4 v) { return sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]); }; static vec4 normalize(vec4 v) { __float n = sqrtf(vec4::length(v)); v[0] /= n; v[1] /= n; v[2] /= n; v[3] /= n; return v; }; static __float dot(vec4 v1, vec4 v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]; }; }; } #endif /* defined(__CubicVR2__vec4__) */ CubicSDR-0.2.3/external/lodepng/000077500000000000000000000000001322677621400163765ustar00rootroot00000000000000CubicSDR-0.2.3/external/lodepng/lodepng.cpp000066400000000000000000006561731322677621400205540ustar00rootroot00000000000000/* LodePNG version 20161127 Copyright (c) 2005-2016 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* The manual and changelog are in the header file "lodepng.h" Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. */ #include "lodepng.h" #include #include #include #if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ const char* LODEPNG_VERSION_STRING = "20161127"; /* This source file is built up in the following large parts. The code sections with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. -Tools for C and common code for PNG and Zlib -C Code for Zlib (huffman, deflate, ...) -C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) -The C++ wrapper around all of the above */ /*The malloc, realloc and free functions defined here with "lodepng_" in front of the name, so that you can easily change them to others related to your platform if needed. Everything else in the code calls these. Pass -DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out #define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and define them in your own project's source files without needing to change lodepng source code. Don't forget to remove "static" if you copypaste them from here.*/ #ifdef LODEPNG_COMPILE_ALLOCATORS static void* lodepng_malloc(size_t size) { return malloc(size); } static void* lodepng_realloc(void* ptr, size_t new_size) { return realloc(ptr, new_size); } static void lodepng_free(void* ptr) { free(ptr); } #else /*LODEPNG_COMPILE_ALLOCATORS*/ void* lodepng_malloc(size_t size); void* lodepng_realloc(void* ptr, size_t new_size); void lodepng_free(void* ptr); #endif /*LODEPNG_COMPILE_ALLOCATORS*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // Tools for C, and common code for PNG and Zlib. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* Often in case of an error a value is assigned to a variable and then it breaks out of a loop (to go to the cleanup phase of a function). This macro does that. It makes the error handling code shorter and more readable. Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); */ #define CERROR_BREAK(errorvar, code)\ {\ errorvar = code;\ break;\ } /*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ #define ERROR_BREAK(code) CERROR_BREAK(error, code) /*Set error var to the error code, and return it.*/ #define CERROR_RETURN_ERROR(errorvar, code)\ {\ errorvar = code;\ return code;\ } /*Try the code, if it returns error, also return the error.*/ #define CERROR_TRY_RETURN(call)\ {\ unsigned error = call;\ if(error) return error;\ } /*Set error var to the error code, and return from the void function.*/ #define CERROR_RETURN(errorvar, code)\ {\ errorvar = code;\ return;\ } /* About uivector, ucvector and string: -All of them wrap dynamic arrays or text strings in a similar way. -LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. -The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. -They're not used in the interface, only internally in this file as static functions. -As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. */ #ifdef LODEPNG_COMPILE_ZLIB /*dynamic vector of unsigned ints*/ typedef struct uivector { unsigned* data; size_t size; /*size in number of unsigned longs*/ size_t allocsize; /*allocated size in bytes*/ } uivector; static void uivector_cleanup(void* p) { ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; lodepng_free(((uivector*)p)->data); ((uivector*)p)->data = NULL; } /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned uivector_reserve(uivector* p, size_t allocsize) { if(allocsize > p->allocsize) { size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned*)data; } else return 0; /*error: not enough memory*/ } return 1; } /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned uivector_resize(uivector* p, size_t size) { if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; p->size = size; return 1; /*success*/ } /*resize and give all new elements the value*/ static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) { size_t oldsize = p->size, i; if(!uivector_resize(p, size)) return 0; for(i = oldsize; i < size; ++i) p->data[i] = value; return 1; } static void uivector_init(uivector* p) { p->data = NULL; p->size = p->allocsize = 0; } #ifdef LODEPNG_COMPILE_ENCODER /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned uivector_push_back(uivector* p, unsigned c) { if(!uivector_resize(p, p->size + 1)) return 0; p->data[p->size - 1] = c; return 1; } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ /* /////////////////////////////////////////////////////////////////////////// */ /*dynamic vector of unsigned chars*/ typedef struct ucvector { unsigned char* data; size_t size; /*used size*/ size_t allocsize; /*allocated size*/ } ucvector; /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned ucvector_reserve(ucvector* p, size_t allocsize) { if(allocsize > p->allocsize) { size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned char*)data; } else return 0; /*error: not enough memory*/ } return 1; } /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned ucvector_resize(ucvector* p, size_t size) { if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; p->size = size; return 1; /*success*/ } #ifdef LODEPNG_COMPILE_PNG static void ucvector_cleanup(void* p) { ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0; lodepng_free(((ucvector*)p)->data); ((ucvector*)p)->data = NULL; } static void ucvector_init(ucvector* p) { p->data = NULL; p->size = p->allocsize = 0; } #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ZLIB /*you can both convert from vector to buffer&size and vica versa. If you use init_buffer to take over a buffer and size, it is not needed to use cleanup*/ static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) { p->data = buffer; p->allocsize = p->size = size; } #endif /*LODEPNG_COMPILE_ZLIB*/ #if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER) /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned ucvector_push_back(ucvector* p, unsigned char c) { if(!ucvector_resize(p, p->size + 1)) return 0; p->data[p->size - 1] = c; return 1; } #endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*returns 1 if success, 0 if failure ==> nothing done*/ static unsigned string_resize(char** out, size_t size) { char* data = (char*)lodepng_realloc(*out, size + 1); if(data) { data[size] = 0; /*null termination char*/ *out = data; } return data != 0; } /*init a {char*, size_t} pair for use as string*/ static void string_init(char** out) { *out = NULL; string_resize(out, 0); } /*free the above pair again*/ static void string_cleanup(char** out) { lodepng_free(*out); *out = NULL; } static void string_set(char** out, const char* in) { size_t insize = strlen(in), i; if(string_resize(out, insize)) { for(i = 0; i != insize; ++i) { (*out)[i] = in[i]; } } } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_PNG*/ /* ////////////////////////////////////////////////////////////////////////// */ unsigned lodepng_read32bitInt(const unsigned char* buffer) { return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); } #if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) /*buffer must have at least 4 allocated bytes available*/ static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) { buffer[0] = (unsigned char)((value >> 24) & 0xff); buffer[1] = (unsigned char)((value >> 16) & 0xff); buffer[2] = (unsigned char)((value >> 8) & 0xff); buffer[3] = (unsigned char)((value ) & 0xff); } #endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ #ifdef LODEPNG_COMPILE_ENCODER static void lodepng_add32bitInt(ucvector* buffer, unsigned value) { ucvector_resize(buffer, buffer->size + 4); /*todo: give error if resize failed*/ lodepng_set32bitInt(&buffer->data[buffer->size - 4], value); } #endif /*LODEPNG_COMPILE_ENCODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* / File IO / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_DISK /* returns negative value on error. This should be pure C compatible, so no fstat. */ static long lodepng_filesize(const char* filename) { FILE* file; long size; file = fopen(filename, "rb"); if(!file) return -1; if(fseek(file, 0, SEEK_END) != 0) { fclose(file); return -1; } size = ftell(file); /* It may give LONG_MAX as directory size, this is invalid for us. */ if(size == LONG_MAX) size = -1; fclose(file); return size; } /* load file into buffer that already has the correct allocated size. Returns error code.*/ static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) { FILE* file; size_t readsize; file = fopen(filename, "rb"); if(!file) return 78; readsize = fread(out, 1, size, file); fclose(file); if (readsize != size) return 78; return 0; } unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) { long size = lodepng_filesize(filename); if (size < 0) return 78; *outsize = (size_t)size; *out = (unsigned char*)lodepng_malloc((size_t)size); if(!(*out) && size > 0) return 83; /*the above malloc failed*/ return lodepng_buffer_file(*out, (size_t)size, filename); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) { FILE* file; file = fopen(filename, "wb" ); if(!file) return 79; fwrite((char*)buffer , 1 , buffersize, file); fclose(file); return 0; } #endif /*LODEPNG_COMPILE_DISK*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // End of common code and tools. Begin of Zlib related code. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_ENCODER /*TODO: this ignores potential out of memory errors*/ #define addBitToStream(/*size_t**/ bitpointer, /*ucvector**/ bitstream, /*unsigned char*/ bit)\ {\ /*add a new byte at the end*/\ if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ ++(*bitpointer);\ } static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) { size_t i; for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); } static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) { size_t i; for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); } #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_DECODER #define READBIT(bitpointer, bitstream) ((bitstream[bitpointer >> 3] >> (bitpointer & 0x7)) & (unsigned char)1) static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); ++(*bitpointer); return result; } static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { unsigned result = 0, i; for(i = 0; i != nbits; ++i) { result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; ++(*bitpointer); } return result; } #endif /*LODEPNG_COMPILE_DECODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* / Deflate - Huffman / */ /* ////////////////////////////////////////////////////////////////////////// */ #define FIRST_LENGTH_CODE_INDEX 257 #define LAST_LENGTH_CODE_INDEX 285 /*256 literals, the end code, some length codes, and 2 unused codes*/ #define NUM_DEFLATE_CODE_SYMBOLS 288 /*the distance codes have their own symbols, 30 used, 2 unused*/ #define NUM_DISTANCE_SYMBOLS 32 /*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ #define NUM_CODE_LENGTH_CODES 19 /*the base lengths represented by codes 257-285*/ static const unsigned LENGTHBASE[29] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; /*the extra bits used by codes 257-285 (added to base length)*/ static const unsigned LENGTHEXTRA[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; /*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ static const unsigned DISTANCEBASE[30] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; /*the extra bits of backwards distances (added to base)*/ static const unsigned DISTANCEEXTRA[30] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /*the order in which "code length alphabet code lengths" are stored, out of this the huffman tree of the dynamic huffman tree lengths is generated*/ static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* ////////////////////////////////////////////////////////////////////////// */ /* Huffman tree struct, containing multiple representations of the tree */ typedef struct HuffmanTree { unsigned* tree2d; unsigned* tree1d; unsigned* lengths; /*the lengths of the codes of the 1d-tree*/ unsigned maxbitlen; /*maximum number of bits a single code can get*/ unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ } HuffmanTree; /*function used for debug purposes to draw the tree in ascii art with C++*/ /* static void HuffmanTree_draw(HuffmanTree* tree) { std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; for(size_t i = 0; i != tree->tree1d.size; ++i) { if(tree->lengths.data[i]) std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; } std::cout << std::endl; }*/ static void HuffmanTree_init(HuffmanTree* tree) { tree->tree2d = 0; tree->tree1d = 0; tree->lengths = 0; } static void HuffmanTree_cleanup(HuffmanTree* tree) { lodepng_free(tree->tree2d); lodepng_free(tree->tree1d); lodepng_free(tree->lengths); } /*the tree representation used by the decoder. return value is error*/ static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) { unsigned nodefilled = 0; /*up to which node it is filled*/ unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/ unsigned n, i; tree->tree2d = (unsigned*)lodepng_malloc(tree->numcodes * 2 * sizeof(unsigned)); if(!tree->tree2d) return 83; /*alloc fail*/ /* convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means uninited, a value >= numcodes is an address to another bit, a value < numcodes is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as many columns as codes - 1. A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. Here, the internal nodes are stored (what their 0 and 1 option point to). There is only memory for such good tree currently, if there are more nodes (due to too long length codes), error 55 will happen */ for(n = 0; n < tree->numcodes * 2; ++n) { tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ } for(n = 0; n < tree->numcodes; ++n) /*the codes*/ { for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ { unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); /*oversubscribed, see comment in lodepng_error_text*/ if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ { if(i + 1 == tree->lengths[n]) /*last bit*/ { tree->tree2d[2 * treepos + bit] = n; /*put the current code in it*/ treepos = 0; } else { /*put address of the next step in here, first that address has to be found of course (it's just nodefilled + 1)...*/ ++nodefilled; /*addresses encoded with numcodes added to it*/ tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; treepos = nodefilled; } } else treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; } } for(n = 0; n < tree->numcodes * 2; ++n) { if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ } return 0; } /* Second step for the ...makeFromLengths and ...makeFromFrequencies functions. numcodes, lengths and maxbitlen must already be filled in correctly. return value is error. */ static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) { uivector blcount; uivector nextcode; unsigned error = 0; unsigned bits, n; uivector_init(&blcount); uivector_init(&nextcode); tree->tree1d = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); if(!tree->tree1d) error = 83; /*alloc fail*/ if(!uivector_resizev(&blcount, tree->maxbitlen + 1, 0) || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0)) error = 83; /*alloc fail*/ if(!error) { /*step 1: count number of instances of each code length*/ for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; /*step 2: generate the nextcode values*/ for(bits = 1; bits <= tree->maxbitlen; ++bits) { nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; } /*step 3: generate all the codes*/ for(n = 0; n != tree->numcodes; ++n) { if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; } } uivector_cleanup(&blcount); uivector_cleanup(&nextcode); if(!error) return HuffmanTree_make2DTree(tree); else return error; } /* given the code lengths (as stored in the PNG file), generate the tree as defined by Deflate. maxbitlen is the maximum bits that a code in the tree can have. return value is error. */ static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen) { unsigned i; tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); if(!tree->lengths) return 83; /*alloc fail*/ for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->maxbitlen = maxbitlen; return HuffmanTree_makeFromLengths2(tree); } #ifdef LODEPNG_COMPILE_ENCODER /*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ /*chain node for boundary package merge*/ typedef struct BPMNode { int weight; /*the sum of all weights in this chain*/ unsigned index; /*index of this leaf node (called "count" in the paper)*/ struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ int in_use; } BPMNode; /*lists of chains*/ typedef struct BPMLists { /*memory pool*/ unsigned memsize; BPMNode* memory; unsigned numfree; unsigned nextfree; BPMNode** freelist; /*two heads of lookahead chains per list*/ unsigned listsize; BPMNode** chains0; BPMNode** chains1; } BPMLists; /*creates a new chain node with the given parameters, from the memory in the lists */ static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) { unsigned i; BPMNode* result; /*memory full, so garbage collect*/ if(lists->nextfree >= lists->numfree) { /*mark only those that are in use*/ for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; for(i = 0; i != lists->listsize; ++i) { BPMNode* node; for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; } /*collect those that are free*/ lists->numfree = 0; for(i = 0; i != lists->memsize; ++i) { if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; } lists->nextfree = 0; } result = lists->freelist[lists->nextfree++]; result->weight = weight; result->index = index; result->tail = tail; return result; } /*sort the leaves with stable mergesort*/ static void bpmnode_sort(BPMNode* leaves, size_t num) { BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); size_t width, counter = 0; for(width = 1; width < num; width *= 2) { BPMNode* a = (counter & 1) ? mem : leaves; BPMNode* b = (counter & 1) ? leaves : mem; size_t p; for(p = 0; p < num; p += 2 * width) { size_t q = (p + width > num) ? num : (p + width); size_t r = (p + 2 * width > num) ? num : (p + 2 * width); size_t i = p, j = q, k; for(k = p; k < r; k++) { if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; else b[k] = a[j++]; } } counter++; } if(counter & 1) memcpy(leaves, mem, sizeof(*leaves) * num); lodepng_free(mem); } /*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) { unsigned lastindex = lists->chains1[c]->index; if(c == 0) { if(lastindex >= numpresent) return; lists->chains0[c] = lists->chains1[c]; lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); } else { /*sum of the weights of the head nodes of the previous lookahead chains.*/ int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; lists->chains0[c] = lists->chains1[c]; if(lastindex < numpresent && sum > leaves[lastindex].weight) { lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); return; } lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); /*in the end we are only interested in the chain of the last list, so no need to recurse if we're at the last one (this gives measurable speedup)*/ if(num + 1 < (int)(2 * numpresent - 2)) { boundaryPM(lists, leaves, numpresent, c - 1, num); boundaryPM(lists, leaves, numpresent, c - 1, num); } } } unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, size_t numcodes, unsigned maxbitlen) { unsigned error = 0; unsigned i; size_t numpresent = 0; /*number of symbols with non-zero frequency*/ BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ if((1u << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/ leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); if(!leaves) return 83; /*alloc fail*/ for(i = 0; i != numcodes; ++i) { if(frequencies[i] > 0) { leaves[numpresent].weight = (int)frequencies[i]; leaves[numpresent].index = i; ++numpresent; } } for(i = 0; i != numcodes; ++i) lengths[i] = 0; /*ensure at least two present symbols. There should be at least one symbol according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To make these work as well ensure there are at least two symbols. The Package-Merge code below also doesn't work correctly if there's only one symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ if(numpresent == 0) { lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ } else if(numpresent == 1) { lengths[leaves[0].index] = 1; lengths[leaves[0].index == 0 ? 1 : 0] = 1; } else { BPMLists lists; BPMNode* node; bpmnode_sort(leaves, numpresent); lists.listsize = maxbitlen; lists.memsize = 2 * maxbitlen * (maxbitlen + 1); lists.nextfree = 0; lists.numfree = lists.memsize; lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ if(!error) { for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; bpmnode_create(&lists, leaves[0].weight, 1, 0); bpmnode_create(&lists, leaves[1].weight, 2, 0); for(i = 0; i != lists.listsize; ++i) { lists.chains0[i] = &lists.memory[0]; lists.chains1[i] = &lists.memory[1]; } /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) { for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; } } lodepng_free(lists.memory); lodepng_free(lists.freelist); lodepng_free(lists.chains0); lodepng_free(lists.chains1); } lodepng_free(leaves); return error; } /*Create the Huffman tree given the symbol frequencies*/ static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, size_t mincodes, size_t numcodes, unsigned maxbitlen) { unsigned error = 0; while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ tree->maxbitlen = maxbitlen; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); if(!tree->lengths) return 83; /*alloc fail*/ /*initialize all lengths to 0*/ memset(tree->lengths, 0, numcodes * sizeof(unsigned)); error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); if(!error) error = HuffmanTree_makeFromLengths2(tree); return error; } static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) { return tree->tree1d[index]; } static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) { return tree->lengths[index]; } #endif /*LODEPNG_COMPILE_ENCODER*/ /*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ static unsigned generateFixedLitLenTree(HuffmanTree* tree) { unsigned i, error = 0; unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); if(!bitlen) return 83; /*alloc fail*/ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ for(i = 0; i <= 143; ++i) bitlen[i] = 8; for(i = 144; i <= 255; ++i) bitlen[i] = 9; for(i = 256; i <= 279; ++i) bitlen[i] = 7; for(i = 280; i <= 287; ++i) bitlen[i] = 8; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); lodepng_free(bitlen); return error; } /*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ static unsigned generateFixedDistanceTree(HuffmanTree* tree) { unsigned i, error = 0; unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen) return 83; /*alloc fail*/ /*there are 32 distance codes, but 30-31 are unused*/ for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); lodepng_free(bitlen); return error; } #ifdef LODEPNG_COMPILE_DECODER /* returns the code, or (unsigned)(-1) if error happened inbitlength is the length of the complete buffer, in bits (so its byte length times 8) */ static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp, const HuffmanTree* codetree, size_t inbitlength) { unsigned treepos = 0, ct; for(;;) { if(*bp >= inbitlength) return (unsigned)(-1); /*error: end of input memory reached without endcode*/ /* decode the symbol from the tree. The "readBitFromStream" code is inlined in the expression below because this is the biggest bottleneck while decoding */ ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; ++(*bp); if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ if(treepos >= codetree->numcodes) return (unsigned)(-1); /*error: it appeared outside the codetree*/ } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_DECODER /* ////////////////////////////////////////////////////////////////////////// */ /* / Inflator (Decompressor) / */ /* ////////////////////////////////////////////////////////////////////////// */ /*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) { /*TODO: check for out of memory errors*/ generateFixedLitLenTree(tree_ll); generateFixedDistanceTree(tree_d); } /*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, const unsigned char* in, size_t* bp, size_t inlength) { /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ unsigned error = 0; unsigned n, HLIT, HDIST, HCLEN, i; size_t inbitlength = inlength * 8; /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ unsigned* bitlen_ll = 0; /*lit,len code lengths*/ unsigned* bitlen_d = 0; /*dist code lengths*/ /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ unsigned* bitlen_cl = 0; HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ HLIT = readBitsFromStream(bp, in, 5) + 257; /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ HDIST = readBitsFromStream(bp, in, 5) + 1; /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ HCLEN = readBitsFromStream(bp, in, 4) + 4; if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ HuffmanTree_init(&tree_cl); while(!error) { /*read the code length codes out of 3 * (amount of code length codes) bits*/ bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) { if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ } error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); if(error) break; /*now we can use this tree to read the lengths for the tree that this function will return*/ bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ i = 0; while(i < HLIT + HDIST) { unsigned code = huffmanDecodeSymbol(in, bp, &tree_cl, inbitlength); if(code <= 15) /*a length code*/ { if(i < HLIT) bitlen_ll[i] = code; else bitlen_d[i - HLIT] = code; ++i; } else if(code == 16) /*repeat previous*/ { unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ unsigned value; /*set value to the previous code*/ if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 2); if(i < HLIT + 1) value = bitlen_ll[i - 1]; else value = bitlen_d[i - HLIT - 1]; /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = value; else bitlen_d[i - HLIT] = value; ++i; } } else if(code == 17) /*repeat "0" 3-10 times*/ { unsigned replength = 3; /*read in the bits that indicate repeat length*/ if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 3); /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; ++i; } } else if(code == 18) /*repeat "0" 11-138 times*/ { unsigned replength = 11; /*read in the bits that indicate repeat length*/ if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 7); /*repeat this value in the next lengths*/ for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; ++i; } } else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ { if(code == (unsigned)(-1)) { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ error = (*bp) > inbitlength ? 10 : 11; } else error = 16; /*unexisting code, this can never happen*/ break; } } if(error) break; if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); if(error) break; error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); break; /*end of error-while*/ } lodepng_free(bitlen_cl); lodepng_free(bitlen_ll); lodepng_free(bitlen_d); HuffmanTree_cleanup(&tree_cl); return error; } /*inflate a block with dynamic of fixed Huffman tree*/ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength, unsigned btype) { unsigned error = 0; HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ HuffmanTree tree_d; /*the huffman tree for distance codes*/ size_t inbitlength = inlength * 8; HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d); else if(btype == 2) error = getTreeInflateDynamic(&tree_ll, &tree_d, in, bp, inlength); while(!error) /*decode all symbols until end reached, breaks at end code*/ { /*code_ll is literal, length or end code*/ unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength); if(code_ll <= 255) /*literal symbol*/ { /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); out->data[*pos] = (unsigned char)code_ll; ++(*pos); } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { unsigned code_d, distance; unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ size_t start, forward, backward, length; /*part 1: get length base*/ length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; /*part 2: get extra bits and add the value of that to length*/ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ length += readBitsFromStream(bp, in, numextrabits_l); /*part 3: get distance code*/ code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength); if(code_d > 29) { if(code_ll == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ error = (*bp) > inlength * 8 ? 10 : 11; } else error = 18; /*error: invalid distance code (30-31 are never used)*/ break; } distance = DISTANCEBASE[code_d]; /*part 4: get extra bits from distance*/ numextrabits_d = DISTANCEEXTRA[code_d]; if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ distance += readBitsFromStream(bp, in, numextrabits_d); /*part 5: fill in all the out[n] values based on the length and dist*/ start = (*pos); if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ backward = start - distance; if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); if (distance < length) { for(forward = 0; forward < length; ++forward) { out->data[(*pos)++] = out->data[backward++]; } } else { memcpy(out->data + *pos, out->data + backward, length); *pos += length; } } else if(code_ll == 256) { break; /*end code, break the loop*/ } else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ error = ((*bp) > inlength * 8) ? 10 : 11; break; } } HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); return error; } static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) { size_t p; unsigned LEN, NLEN, n, error = 0; /*go to first boundary of byte*/ while(((*bp) & 0x7) != 0) ++(*bp); p = (*bp) / 8; /*byte position*/ /*read LEN (2 bytes) and NLEN (2 bytes)*/ if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ LEN = in[p] + 256u * in[p + 1]; p += 2; NLEN = in[p] + 256u * in[p + 1]; p += 2; /*check if 16-bit NLEN is really the one's complement of LEN*/ if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/ if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ /*read the literal data: LEN bytes are now stored in the out buffer*/ if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; (*bp) = p * 8; return error; } static unsigned lodepng_inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/ size_t bp = 0; unsigned BFINAL = 0; size_t pos = 0; /*byte position in the out buffer*/ unsigned error = 0; (void)settings; while(!BFINAL) { unsigned BTYPE; if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/ BFINAL = readBitFromStream(&bp, in); BTYPE = 1u * readBitFromStream(&bp, in); BTYPE += 2u * readBitFromStream(&bp, in); if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/ else error = inflateHuffmanBlock(out, in, &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ if(error) return error; } return error; } unsigned lodepng_inflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned error; ucvector v; ucvector_init_buffer(&v, *out, *outsize); error = lodepng_inflatev(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } static unsigned inflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(settings->custom_inflate) { return settings->custom_inflate(out, outsize, in, insize, settings); } else { return lodepng_inflate(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* ////////////////////////////////////////////////////////////////////////// */ /* / Deflator (Compressor) / */ /* ////////////////////////////////////////////////////////////////////////// */ static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; /*bitlen is the size in bits of the code*/ static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen) { addBitsToStreamReversed(bp, compressed, code, bitlen); } /*search the index in the array, that has the largest value smaller than or equal to the given value, given array must be sorted (if no value is smaller, it returns the size of the given array)*/ static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) { /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ size_t left = 1; size_t right = array_size - 1; while(left <= right) { size_t mid = (left + right) >> 1; if (array[mid] >= value) right = mid - 1; else left = mid + 1; } if(left >= array_size || array[left] > value) left--; return left; } static void addLengthDistance(uivector* values, size_t length, size_t distance) { /*values in encoded vector are those used by deflate: 0-255: literal bytes 256: end 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) 286-287: invalid*/ unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX); uivector_push_back(values, extra_length); uivector_push_back(values, dist_code); uivector_push_back(values, extra_distance); } /*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 bytes as input because 3 is the minimum match length for deflate*/ static const unsigned HASH_NUM_VALUES = 65536; static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ typedef struct Hash { int* head; /*hash value to head circular pos - can be outdated if went around window*/ /*circular pos to prev circular pos*/ unsigned short* chain; int* val; /*circular pos to hash value*/ /*TODO: do this not only for zeros but for any repeated byte. However for PNG it's always going to be the zeros that dominate, so not important for PNG*/ int* headz; /*similar to head, but for chainz*/ unsigned short* chainz; /*those with same amount of zeros*/ unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ } Hash; static unsigned hash_init(Hash* hash, unsigned windowsize) { unsigned i; hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) { return 83; /*alloc fail*/ } /*initialize hash table*/ for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; for(i = 0; i != windowsize; ++i) hash->val[i] = -1; for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ return 0; } static void hash_cleanup(Hash* hash) { lodepng_free(hash->head); lodepng_free(hash->val); lodepng_free(hash->chain); lodepng_free(hash->zeros); lodepng_free(hash->headz); lodepng_free(hash->chainz); } static unsigned getHash(const unsigned char* data, size_t size, size_t pos) { unsigned result = 0; if(pos + 2 < size) { /*A simple shift and xor hash is used. Since the data of PNGs is dominated by zeroes due to the filters, a better hash does not have a significant effect on speed in traversing the chain, and causes more time spend on calculating the hash.*/ result ^= (unsigned)(data[pos + 0] << 0u); result ^= (unsigned)(data[pos + 1] << 4u); result ^= (unsigned)(data[pos + 2] << 8u); } else { size_t amount, i; if(pos >= size) return 0; amount = size - pos; for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); } return result & HASH_BIT_MASK; } static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) { const unsigned char* start = data + pos; const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; if(end > data + size) end = data + size; data = start; while(data != end && *data == 0) ++data; /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ return (unsigned)(data - start); } /*wpos = pos & (windowsize - 1)*/ static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) { hash->val[wpos] = (int)hashval; if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; hash->head[hashval] = wpos; hash->zeros[wpos] = numzeros; if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; hash->headz[numzeros] = wpos; } /* LZ77-encode the data. Return value is error code. The input are raw bytes, the output is in the form of unsigned integers with codes representing for example literal bytes, or length/distance pairs. It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a sliding window (of windowsize) is used, and all past bytes in that window can be used as the "dictionary". A brute force search through all possible distances would be slow, and this hash technique is one out of several ways to speed this up. */ static unsigned encodeLZ77(uivector* out, Hash* hash, const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, unsigned minmatch, unsigned nicematch, unsigned lazymatching) { size_t pos; unsigned i, error = 0; /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8; unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ unsigned numzeros = 0; unsigned offset; /*the offset represents the distance in LZ77 terminology*/ unsigned length; unsigned lazy = 0; unsigned lazylength = 0, lazyoffset = 0; unsigned hashval; unsigned current_offset, current_length; unsigned prev_offset; const unsigned char *lastptr, *foreptr, *backptr; unsigned hashpos; if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; for(pos = inpos; pos < insize; ++pos) { size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ unsigned chainlength = 0; hashval = getHash(in, insize, pos); if(usezeros && hashval == 0) { if(numzeros == 0) numzeros = countZeros(in, insize, pos); else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { numzeros = 0; } updateHashChain(hash, wpos, hashval, numzeros); /*the length and offset found for the current position*/ length = 0; offset = 0; hashpos = hash->chain[wpos]; lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; /*search for the longest string*/ prev_offset = 0; for(;;) { if(chainlength++ >= maxchainlength) break; current_offset = hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize; if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ prev_offset = current_offset; if(current_offset > 0) { /*test the next characters*/ foreptr = &in[pos]; backptr = &in[pos - current_offset]; /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ if(numzeros >= 3) { unsigned skip = hash->zeros[hashpos]; if(skip > numzeros) skip = numzeros; backptr += skip; foreptr += skip; } while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ { ++backptr; ++foreptr; } current_length = (unsigned)(foreptr - &in[pos]); if(current_length > length) { length = current_length; /*the longest length*/ offset = current_offset; /*the offset that is related to this longest length*/ /*jump out once a length of max length is found (speed gain). This also jumps out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ if(current_length >= nicematch) break; } } if(hashpos == hash->chain[hashpos]) break; if(numzeros >= 3 && length > numzeros) { hashpos = hash->chainz[hashpos]; if(hash->zeros[hashpos] != numzeros) break; } else { hashpos = hash->chain[hashpos]; /*outdated hash value, happens if particular value was not encountered in whole last window*/ if(hash->val[hashpos] != (int)hashval) break; } } if(lazymatching) { if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) { lazy = 1; lazylength = length; lazyoffset = offset; continue; /*try the next byte*/ } if(lazy) { lazy = 0; if(pos == 0) ERROR_BREAK(81); if(length > lazylength + 1) { /*push the previous character as literal*/ if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); } else { length = lazylength; offset = lazyoffset; hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ hash->headz[numzeros] = -1; /*idem*/ --pos; } } } if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); /*encode it as length/distance pair or literal value*/ if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ { if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); } else if(length < minmatch || (length == 3 && offset > 4096)) { /*compensate for the fact that longer offsets have more extra bits, a length of only 3 may be not worth it then*/ if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); } else { addLengthDistance(out, length, offset); for(i = 1; i < length; ++i) { ++pos; wpos = pos & (windowsize - 1); hashval = getHash(in, insize, pos); if(usezeros && hashval == 0) { if(numzeros == 0) numzeros = countZeros(in, insize, pos); else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { numzeros = 0; } updateHashChain(hash, wpos, hashval, numzeros); } } } /*end of the loop through each character of input*/ return error; } /* /////////////////////////////////////////////////////////////////////////// */ static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) { /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; unsigned datapos = 0; for(i = 0; i != numdeflateblocks; ++i) { unsigned BFINAL, BTYPE, LEN, NLEN; unsigned char firstbyte; BFINAL = (i == numdeflateblocks - 1); BTYPE = 0; firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1)); ucvector_push_back(out, firstbyte); LEN = 65535; if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; NLEN = 65535 - LEN; ucvector_push_back(out, (unsigned char)(LEN & 255)); ucvector_push_back(out, (unsigned char)(LEN >> 8)); ucvector_push_back(out, (unsigned char)(NLEN & 255)); ucvector_push_back(out, (unsigned char)(NLEN >> 8)); /*Decompressed data*/ for(j = 0; j < 65535 && datapos < datasize; ++j) { ucvector_push_back(out, data[datapos++]); } } return 0; } /* write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. tree_ll: the tree for lit and len codes. tree_d: the tree for distance codes. */ static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded, const HuffmanTree* tree_ll, const HuffmanTree* tree_d) { size_t i = 0; for(i = 0; i != lz77_encoded->size; ++i) { unsigned val = lz77_encoded->data[i]; addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); if(val > 256) /*for a length code, 3 more things have to be added*/ { unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; unsigned length_extra_bits = lz77_encoded->data[++i]; unsigned distance_code = lz77_encoded->data[++i]; unsigned distance_index = distance_code; unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; unsigned distance_extra_bits = lz77_encoded->data[++i]; addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits); addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_d, distance_code), HuffmanTree_getLength(tree_d, distance_code)); addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits); } } } /*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, const unsigned char* data, size_t datapos, size_t dataend, const LodePNGCompressSettings* settings, unsigned final) { unsigned error = 0; /* A block is compressed as follows: The PNG data is lz77 encoded, resulting in literal bytes and length/distance pairs. This is then huffman compressed with two huffman trees. One huffman tree is used for the lit and len values ("ll"), another huffman tree is used for the dist values ("d"). These two trees are stored using their code lengths, and to compress even more these code lengths are also run-length encoded and huffman compressed. This gives a huffman tree of code lengths "cl". The code lenghts used to describe this third tree are the code length code lengths ("clcl"). */ /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ uivector lz77_encoded; HuffmanTree tree_ll; /*tree for lit,len values*/ HuffmanTree tree_d; /*tree for distance codes*/ HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ uivector frequencies_ll; /*frequency of lit,len codes*/ uivector frequencies_d; /*frequency of dist codes*/ uivector frequencies_cl; /*frequency of code length codes*/ uivector bitlen_lld; /*lit,len,dist code lenghts (int bits), literally (without repeat codes).*/ uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudemtary run length compression)*/ /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl (these are written as is in the file, it would be crazy to compress these using yet another huffman tree that needs to be represented by yet another set of code lengths)*/ uivector bitlen_cl; size_t datasize = dataend - datapos; /* Due to the huffman compression of huffman tree representations ("two levels"), there are some anologies: bitlen_lld is to tree_cl what data is to tree_ll and tree_d. bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. */ unsigned BFINAL = final; size_t numcodes_ll, numcodes_d, i; unsigned HLIT, HDIST, HCLEN; uivector_init(&lz77_encoded); HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); HuffmanTree_init(&tree_cl); uivector_init(&frequencies_ll); uivector_init(&frequencies_d); uivector_init(&frequencies_cl); uivector_init(&bitlen_lld); uivector_init(&bitlen_lld_e); uivector_init(&bitlen_cl); /*This while loop never loops due to a break at the end, it is here to allow breaking out of it to the cleanup phase on error conditions.*/ while(!error) { if(settings->use_lz77) { error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, settings->minmatch, settings->nicematch, settings->lazymatching); if(error) break; } else { if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ } if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); /*Count the frequencies of lit, len and dist codes*/ for(i = 0; i != lz77_encoded.size; ++i) { unsigned symbol = lz77_encoded.data[i]; ++frequencies_ll.data[symbol]; if(symbol > 256) { unsigned dist = lz77_encoded.data[i + 2]; ++frequencies_d.data[dist]; i += 3; } } frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15); if(error) break; /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15); if(error) break; numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; /*store the code lengths of both generated trees in bitlen_lld*/ for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), 17 (3-10 zeroes), 18 (11-138 zeroes)*/ for(i = 0; i != (unsigned)bitlen_lld.size; ++i) { unsigned j = 0; /*amount of repititions*/ while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ { ++j; /*include the first zero*/ if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ { uivector_push_back(&bitlen_lld_e, 17); uivector_push_back(&bitlen_lld_e, j - 3); } else /*repeat code 18 supports max 138 zeroes*/ { if(j > 138) j = 138; uivector_push_back(&bitlen_lld_e, 18); uivector_push_back(&bitlen_lld_e, j - 11); } i += (j - 1); } else if(j >= 3) /*repeat code for value other than zero*/ { size_t k; unsigned num = j / 6, rest = j % 6; uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); for(k = 0; k < num; ++k) { uivector_push_back(&bitlen_lld_e, 16); uivector_push_back(&bitlen_lld_e, 6 - 3); } if(rest >= 3) { uivector_push_back(&bitlen_lld_e, 16); uivector_push_back(&bitlen_lld_e, rest - 3); } else j -= rest; i += j; } else /*too short to benefit from repeat code*/ { uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); } } /*generate tree_cl, the huffmantree of huffmantrees*/ if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); for(i = 0; i != bitlen_lld_e.size; ++i) { ++frequencies_cl.data[bitlen_lld_e.data[i]]; /*after a repeat code come the bits that specify the number of repetitions, those don't need to be in the frequencies_cl calculation*/ if(bitlen_lld_e.data[i] >= 16) ++i; } error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, frequencies_cl.size, frequencies_cl.size, 7); if(error) break; if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); for(i = 0; i != tree_cl.numcodes; ++i) { /*lenghts of code length tree is in the order as specified by deflate*/ bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); } while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) { /*remove zeros at the end, but minimum size must be 4*/ if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/); } if(error) break; /* Write everything into the output After the BFINAL and BTYPE, the dynamic block consists out of the following: - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN - (HCLEN+4)*3 bits code lengths of code length alphabet - HLIT + 257 code lenghts of lit/length alphabet (encoded using the code length alphabet, + possible repetition codes 16, 17, 18) - HDIST + 1 code lengths of distance alphabet (encoded using the code length alphabet, + possible repetition codes 16, 17, 18) - compressed data - 256 (end code) */ /*Write block type*/ addBitToStream(bp, out, BFINAL); addBitToStream(bp, out, 0); /*first bit of BTYPE "dynamic"*/ addBitToStream(bp, out, 1); /*second bit of BTYPE "dynamic"*/ /*write the HLIT, HDIST and HCLEN values*/ HLIT = (unsigned)(numcodes_ll - 257); HDIST = (unsigned)(numcodes_d - 1); HCLEN = (unsigned)bitlen_cl.size - 4; /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; addBitsToStream(bp, out, HLIT, 5); addBitsToStream(bp, out, HDIST, 5); addBitsToStream(bp, out, HCLEN, 4); /*write the code lenghts of the code length alphabet*/ for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); /*write the lenghts of the lit/len AND the dist alphabet*/ for(i = 0; i != bitlen_lld_e.size; ++i) { addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); /*extra bits of repeat codes*/ if(bitlen_lld_e.data[i] == 16) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 2); else if(bitlen_lld_e.data[i] == 17) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 3); else if(bitlen_lld_e.data[i] == 18) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 7); } /*write the compressed data symbols*/ writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); /*error: the length of the end code 256 must be larger than 0*/ if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64); /*write the end code*/ addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); break; /*end of error-while*/ } /*cleanup*/ uivector_cleanup(&lz77_encoded); HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); HuffmanTree_cleanup(&tree_cl); uivector_cleanup(&frequencies_ll); uivector_cleanup(&frequencies_d); uivector_cleanup(&frequencies_cl); uivector_cleanup(&bitlen_lld_e); uivector_cleanup(&bitlen_lld); uivector_cleanup(&bitlen_cl); return error; } static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, const unsigned char* data, size_t datapos, size_t dataend, const LodePNGCompressSettings* settings, unsigned final) { HuffmanTree tree_ll; /*tree for literal values and length codes*/ HuffmanTree tree_d; /*tree for distance codes*/ unsigned BFINAL = final; unsigned error = 0; size_t i; HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_d); generateFixedLitLenTree(&tree_ll); generateFixedDistanceTree(&tree_d); addBitToStream(bp, out, BFINAL); addBitToStream(bp, out, 1); /*first bit of BTYPE*/ addBitToStream(bp, out, 0); /*second bit of BTYPE*/ if(settings->use_lz77) /*LZ77 encoded*/ { uivector lz77_encoded; uivector_init(&lz77_encoded); error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, settings->minmatch, settings->nicematch, settings->lazymatching); if(!error) writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); uivector_cleanup(&lz77_encoded); } else /*no LZ77, but still will be Huffman compressed*/ { for(i = datapos; i < dataend; ++i) { addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); } } /*add END code*/ if(!error) addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); /*cleanup*/ HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_d); return error; } static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { unsigned error = 0; size_t i, blocksize, numdeflateblocks; size_t bp = 0; /*the bit pointer*/ Hash hash; if(settings->btype > 2) return 61; else if(settings->btype == 0) return deflateNoCompression(out, in, insize); else if(settings->btype == 1) blocksize = insize; else /*if(settings->btype == 2)*/ { /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ blocksize = insize / 8 + 8; if(blocksize < 65536) blocksize = 65536; if(blocksize > 262144) blocksize = 262144; } numdeflateblocks = (insize + blocksize - 1) / blocksize; if(numdeflateblocks == 0) numdeflateblocks = 1; error = hash_init(&hash, settings->windowsize); if(error) return error; for(i = 0; i != numdeflateblocks && !error; ++i) { unsigned final = (i == numdeflateblocks - 1); size_t start = i * blocksize; size_t end = start + blocksize; if(end > insize) end = insize; if(settings->btype == 1) error = deflateFixed(out, &bp, &hash, in, start, end, settings, final); else if(settings->btype == 2) error = deflateDynamic(out, &bp, &hash, in, start, end, settings, final); } hash_cleanup(&hash); return error; } unsigned lodepng_deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { unsigned error; ucvector v; ucvector_init_buffer(&v, *out, *outsize); error = lodepng_deflatev(&v, in, insize, settings); *out = v.data; *outsize = v.size; return error; } static unsigned deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(settings->custom_deflate) { return settings->custom_deflate(out, outsize, in, insize, settings); } else { return lodepng_deflate(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_DECODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* / Adler32 */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) { unsigned s1 = adler & 0xffff; unsigned s2 = (adler >> 16) & 0xffff; while(len > 0) { /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ unsigned amount = len > 5550 ? 5550 : len; len -= amount; while(amount > 0) { s1 += (*data++); s2 += s1; --amount; } s1 %= 65521; s2 %= 65521; } return (s2 << 16) | s1; } /*Return the adler32 of the bytes data[0..len-1]*/ static unsigned adler32(const unsigned char* data, unsigned len) { return update_adler32(1L, data, len); } /* ////////////////////////////////////////////////////////////////////////// */ /* / Zlib / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_DECODER unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { unsigned error = 0; unsigned CM, CINFO, FDICT; if(insize < 2) return 53; /*error, size of zlib data too small*/ /*read information from zlib header*/ if((in[0] * 256 + in[1]) % 31 != 0) { /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ return 24; } CM = in[0] & 15; CINFO = (in[0] >> 4) & 15; /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ FDICT = (in[1] >> 5) & 1; /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ if(CM != 8 || CINFO > 7) { /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ return 25; } if(FDICT != 0) { /*error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary."*/ return 26; } error = inflate(out, outsize, in + 2, insize - 2, settings); if(error) return error; if(!settings->ignore_adler32) { unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); unsigned checksum = adler32(*out, (unsigned)(*outsize)); if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ } return 0; /*no error*/ } static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(settings->custom_zlib) { return settings->custom_zlib(out, outsize, in, insize, settings); } else { return lodepng_zlib_decompress(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { /*initially, *out must be NULL and outsize 0, if you just give some random *out that's pointing to a non allocated buffer, this'll crash*/ ucvector outv; size_t i; unsigned error; unsigned char* deflatedata = 0; size_t deflatesize = 0; /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ unsigned FLEVEL = 0; unsigned FDICT = 0; unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; unsigned FCHECK = 31 - CMFFLG % 31; CMFFLG += FCHECK; /*ucvector-controlled version of the output buffer, for dynamic array*/ ucvector_init_buffer(&outv, *out, *outsize); ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); error = deflate(&deflatedata, &deflatesize, in, insize, settings); if(!error) { unsigned ADLER32 = adler32(in, (unsigned)insize); for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); lodepng_free(deflatedata); lodepng_add32bitInt(&outv, ADLER32); } *out = outv.data; *outsize = outv.size; return error; } /* compress using the default or custom zlib function */ static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(settings->custom_zlib) { return settings->custom_zlib(out, outsize, in, insize, settings); } else { return lodepng_zlib_compress(out, outsize, in, insize, settings); } } #endif /*LODEPNG_COMPILE_ENCODER*/ #else /*no LODEPNG_COMPILE_ZLIB*/ #ifdef LODEPNG_COMPILE_DECODER static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_ENCODER /*this is a good tradeoff between speed and compression ratio*/ #define DEFAULT_WINDOWSIZE 2048 void lodepng_compress_settings_init(LodePNGCompressSettings* settings) { /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ settings->btype = 2; settings->use_lz77 = 1; settings->windowsize = DEFAULT_WINDOWSIZE; settings->minmatch = 3; settings->nicematch = 128; settings->lazymatching = 1; settings->custom_zlib = 0; settings->custom_deflate = 0; settings->custom_context = 0; } const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_DECODER void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { settings->ignore_adler32 = 0; settings->custom_zlib = 0; settings->custom_inflate = 0; settings->custom_context = 0; } const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0}; #endif /*LODEPNG_COMPILE_DECODER*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // End of Zlib related code. Begin of PNG related code. // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_PNG /* ////////////////////////////////////////////////////////////////////////// */ /* / CRC32 / */ /* ////////////////////////////////////////////////////////////////////////// */ #ifndef LODEPNG_NO_COMPILE_CRC /* CRC polynomial: 0xedb88320 */ static unsigned lodepng_crc32_table[256] = { 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u }; /*Return the CRC of the bytes buf[0..len-1].*/ unsigned lodepng_crc32(const unsigned char* data, size_t length) { unsigned r = 0xffffffffu; size_t i; for(i = 0; i < length; ++i) { r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); } return r ^ 0xffffffffu; } #else /* !LODEPNG_NO_COMPILE_CRC */ unsigned lodepng_crc32(const unsigned char* data, size_t length); #endif /* !LODEPNG_NO_COMPILE_CRC */ /* ////////////////////////////////////////////////////////////////////////// */ /* / Reading and writing single bits and bytes from/to stream for LodePNG / */ /* ////////////////////////////////////////////////////////////////////////// */ static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); ++(*bitpointer); return result; } static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { unsigned result = 0; size_t i; for(i = 0 ; i < nbits; ++i) { result <<= 1; result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); } return result; } #ifdef LODEPNG_COMPILE_DECODER static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) { /*the current bit in bitstream must be 0 for this to work*/ if(bit) { /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); } ++(*bitpointer); } #endif /*LODEPNG_COMPILE_DECODER*/ static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) { /*the current bit in bitstream may be 0 or 1 for this to work*/ if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); ++(*bitpointer); } /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG chunks / */ /* ////////////////////////////////////////////////////////////////////////// */ unsigned lodepng_chunk_length(const unsigned char* chunk) { return lodepng_read32bitInt(&chunk[0]); } void lodepng_chunk_type(char type[5], const unsigned char* chunk) { unsigned i; for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; type[4] = 0; /*null termination char*/ } unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) { if(strlen(type) != 4) return 0; return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); } unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) { return((chunk[4] & 32) != 0); } unsigned char lodepng_chunk_private(const unsigned char* chunk) { return((chunk[6] & 32) != 0); } unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) { return((chunk[7] & 32) != 0); } unsigned char* lodepng_chunk_data(unsigned char* chunk) { return &chunk[8]; } const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) { return &chunk[8]; } unsigned lodepng_chunk_check_crc(const unsigned char* chunk) { unsigned length = lodepng_chunk_length(chunk); unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ unsigned checksum = lodepng_crc32(&chunk[4], length + 4); if(CRC != checksum) return 1; else return 0; } void lodepng_chunk_generate_crc(unsigned char* chunk) { unsigned length = lodepng_chunk_length(chunk); unsigned CRC = lodepng_crc32(&chunk[4], length + 4); lodepng_set32bitInt(chunk + 8 + length, CRC); } unsigned char* lodepng_chunk_next(unsigned char* chunk) { unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; return &chunk[total_chunk_length]; } const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) { unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; return &chunk[total_chunk_length]; } unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) { unsigned i; unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; unsigned char *chunk_start, *new_buffer; size_t new_length = (*outlength) + total_chunk_length; if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/ new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); if(!new_buffer) return 83; /*alloc fail*/ (*out) = new_buffer; (*outlength) = new_length; chunk_start = &(*out)[new_length - total_chunk_length]; for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; return 0; } unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, const char* type, const unsigned char* data) { unsigned i; unsigned char *chunk, *new_buffer; size_t new_length = (*outlength) + length + 12; if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/ new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); if(!new_buffer) return 83; /*alloc fail*/ (*out) = new_buffer; (*outlength) = new_length; chunk = &(*out)[(*outlength) - length - 12]; /*1: length*/ lodepng_set32bitInt(chunk, (unsigned)length); /*2: chunk name (4 letters)*/ chunk[4] = (unsigned char)type[0]; chunk[5] = (unsigned char)type[1]; chunk[6] = (unsigned char)type[2]; chunk[7] = (unsigned char)type[3]; /*3: the data*/ for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; /*4: CRC (of the chunkname characters and the data)*/ lodepng_chunk_generate_crc(chunk); return 0; } /* ////////////////////////////////////////////////////////////////////////// */ /* / Color types and such / */ /* ////////////////////////////////////////////////////////////////////////// */ /*return type is a LodePNG error code*/ static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) /*bd = bitdepth*/ { switch(colortype) { case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ case 2: if(!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ case 4: if(!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ case 6: if(!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ default: return 31; } return 0; /*allowed color type / bits combination*/ } static unsigned getNumColorChannels(LodePNGColorType colortype) { switch(colortype) { case 0: return 1; /*grey*/ case 2: return 3; /*RGB*/ case 3: return 1; /*palette*/ case 4: return 2; /*grey + alpha*/ case 6: return 4; /*RGBA*/ } return 0; /*unexisting color type*/ } static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) { /*bits per pixel is amount of channels * bits per channel*/ return getNumColorChannels(colortype) * bitdepth; } /* ////////////////////////////////////////////////////////////////////////// */ void lodepng_color_mode_init(LodePNGColorMode* info) { info->key_defined = 0; info->key_r = info->key_g = info->key_b = 0; info->colortype = LCT_RGBA; info->bitdepth = 8; info->palette = 0; info->palettesize = 0; } void lodepng_color_mode_cleanup(LodePNGColorMode* info) { lodepng_palette_clear(info); } unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) { size_t i; lodepng_color_mode_cleanup(dest); *dest = *source; if(source->palette) { dest->palette = (unsigned char*)lodepng_malloc(1024); if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; } return 0; } static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) { size_t i; if(a->colortype != b->colortype) return 0; if(a->bitdepth != b->bitdepth) return 0; if(a->key_defined != b->key_defined) return 0; if(a->key_defined) { if(a->key_r != b->key_r) return 0; if(a->key_g != b->key_g) return 0; if(a->key_b != b->key_b) return 0; } /*if one of the palette sizes is 0, then we consider it to be the same as the other: it means that e.g. the palette was not given by the user and should be considered the same as the palette inside the PNG.*/ if(1/*a->palettesize != 0 && b->palettesize != 0*/) { if(a->palettesize != b->palettesize) return 0; for(i = 0; i != a->palettesize * 4; ++i) { if(a->palette[i] != b->palette[i]) return 0; } } return 1; } void lodepng_palette_clear(LodePNGColorMode* info) { if(info->palette) lodepng_free(info->palette); info->palette = 0; info->palettesize = 0; } unsigned lodepng_palette_add(LodePNGColorMode* info, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { unsigned char* data; /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with the max of 256 colors, it'll have the exact alloc size*/ if(!info->palette) /*allocate palette if empty*/ { /*room for 256 colors with 4 bytes each*/ data = (unsigned char*)lodepng_realloc(info->palette, 1024); if(!data) return 83; /*alloc fail*/ else info->palette = data; } info->palette[4 * info->palettesize + 0] = r; info->palette[4 * info->palettesize + 1] = g; info->palette[4 * info->palettesize + 2] = b; info->palette[4 * info->palettesize + 3] = a; ++info->palettesize; return 0; } unsigned lodepng_get_bpp(const LodePNGColorMode* info) { /*calculate bits per pixel out of colortype and bitdepth*/ return lodepng_get_bpp_lct(info->colortype, info->bitdepth); } unsigned lodepng_get_channels(const LodePNGColorMode* info) { return getNumColorChannels(info->colortype); } unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) { return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; } unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) { return (info->colortype & 4) != 0; /*4 or 6*/ } unsigned lodepng_is_palette_type(const LodePNGColorMode* info) { return info->colortype == LCT_PALETTE; } unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) { size_t i; for(i = 0; i != info->palettesize; ++i) { if(info->palette[i * 4 + 3] < 255) return 1; } return 0; } unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) { return info->key_defined || lodepng_is_alpha_type(info) || lodepng_has_palette_alpha(info); } size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { /*will not overflow for any color type if roughly w * h < 268435455*/ size_t bpp = lodepng_get_bpp(color); size_t n = w * h; return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { /*will not overflow for any color type if roughly w * h < 268435455*/ size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); size_t n = w * h; return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_DECODER /*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) { /*will not overflow for any color type if roughly w * h < 268435455*/ size_t bpp = lodepng_get_bpp(color); size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; return h * line; } #endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static void LodePNGUnknownChunks_init(LodePNGInfo* info) { unsigned i; for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; } static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) { unsigned i; for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); } static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) { unsigned i; LodePNGUnknownChunks_cleanup(dest); for(i = 0; i != 3; ++i) { size_t j; dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ for(j = 0; j < src->unknown_chunks_size[i]; ++j) { dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; } } return 0; } /******************************************************************************/ static void LodePNGText_init(LodePNGInfo* info) { info->text_num = 0; info->text_keys = NULL; info->text_strings = NULL; } static void LodePNGText_cleanup(LodePNGInfo* info) { size_t i; for(i = 0; i != info->text_num; ++i) { string_cleanup(&info->text_keys[i]); string_cleanup(&info->text_strings[i]); } lodepng_free(info->text_keys); lodepng_free(info->text_strings); } static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { size_t i = 0; dest->text_keys = 0; dest->text_strings = 0; dest->text_num = 0; for(i = 0; i != source->text_num; ++i) { CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); } return 0; } void lodepng_clear_text(LodePNGInfo* info) { LodePNGText_cleanup(info); } unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) { char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); if(!new_keys || !new_strings) { lodepng_free(new_keys); lodepng_free(new_strings); return 83; /*alloc fail*/ } ++info->text_num; info->text_keys = new_keys; info->text_strings = new_strings; string_init(&info->text_keys[info->text_num - 1]); string_set(&info->text_keys[info->text_num - 1], key); string_init(&info->text_strings[info->text_num - 1]); string_set(&info->text_strings[info->text_num - 1], str); return 0; } /******************************************************************************/ static void LodePNGIText_init(LodePNGInfo* info) { info->itext_num = 0; info->itext_keys = NULL; info->itext_langtags = NULL; info->itext_transkeys = NULL; info->itext_strings = NULL; } static void LodePNGIText_cleanup(LodePNGInfo* info) { size_t i; for(i = 0; i != info->itext_num; ++i) { string_cleanup(&info->itext_keys[i]); string_cleanup(&info->itext_langtags[i]); string_cleanup(&info->itext_transkeys[i]); string_cleanup(&info->itext_strings[i]); } lodepng_free(info->itext_keys); lodepng_free(info->itext_langtags); lodepng_free(info->itext_transkeys); lodepng_free(info->itext_strings); } static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { size_t i = 0; dest->itext_keys = 0; dest->itext_langtags = 0; dest->itext_transkeys = 0; dest->itext_strings = 0; dest->itext_num = 0; for(i = 0; i != source->itext_num; ++i) { CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], source->itext_transkeys[i], source->itext_strings[i])); } return 0; } void lodepng_clear_itext(LodePNGInfo* info) { LodePNGIText_cleanup(info); } unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, const char* transkey, const char* str) { char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); if(!new_keys || !new_langtags || !new_transkeys || !new_strings) { lodepng_free(new_keys); lodepng_free(new_langtags); lodepng_free(new_transkeys); lodepng_free(new_strings); return 83; /*alloc fail*/ } ++info->itext_num; info->itext_keys = new_keys; info->itext_langtags = new_langtags; info->itext_transkeys = new_transkeys; info->itext_strings = new_strings; string_init(&info->itext_keys[info->itext_num - 1]); string_set(&info->itext_keys[info->itext_num - 1], key); string_init(&info->itext_langtags[info->itext_num - 1]); string_set(&info->itext_langtags[info->itext_num - 1], langtag); string_init(&info->itext_transkeys[info->itext_num - 1]); string_set(&info->itext_transkeys[info->itext_num - 1], transkey); string_init(&info->itext_strings[info->itext_num - 1]); string_set(&info->itext_strings[info->itext_num - 1], str); return 0; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ void lodepng_info_init(LodePNGInfo* info) { lodepng_color_mode_init(&info->color); info->interlace_method = 0; info->compression_method = 0; info->filter_method = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS info->background_defined = 0; info->background_r = info->background_g = info->background_b = 0; LodePNGText_init(info); LodePNGIText_init(info); info->time_defined = 0; info->phys_defined = 0; LodePNGUnknownChunks_init(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } void lodepng_info_cleanup(LodePNGInfo* info) { lodepng_color_mode_cleanup(&info->color); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS LodePNGText_cleanup(info); LodePNGIText_cleanup(info); LodePNGUnknownChunks_cleanup(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) { lodepng_info_cleanup(dest); *dest = *source; lodepng_color_mode_init(&dest->color); CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); LodePNGUnknownChunks_init(dest); CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ return 0; } void lodepng_info_swap(LodePNGInfo* a, LodePNGInfo* b) { LodePNGInfo temp = *a; *a = *b; *b = temp; } /* ////////////////////////////////////////////////////////////////////////// */ /*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) { unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ unsigned p = index & m; in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ in = in << (bits * (m - p)); if(p == 0) out[index * bits / 8] = in; else out[index * bits / 8] |= in; } typedef struct ColorTree ColorTree; /* One node of a color tree This is the data structure used to count the number of unique colors and to get a palette index for a color. It's like an octree, but because the alpha channel is used too, each node has 16 instead of 8 children. */ struct ColorTree { ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ int index; /*the payload. Only has a meaningful value if this is in the last level*/ }; static void color_tree_init(ColorTree* tree) { int i; for(i = 0; i != 16; ++i) tree->children[i] = 0; tree->index = -1; } static void color_tree_cleanup(ColorTree* tree) { int i; for(i = 0; i != 16; ++i) { if(tree->children[i]) { color_tree_cleanup(tree->children[i]); lodepng_free(tree->children[i]); } } } /*returns -1 if color not present, its index otherwise*/ static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { int bit = 0; for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) return -1; else tree = tree->children[i]; } return tree ? tree->index : -1; } #ifdef LODEPNG_COMPILE_ENCODER static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { return color_tree_get(tree, r, g, b, a) >= 0; } #endif /*LODEPNG_COMPILE_ENCODER*/ /*color is not allowed to already exist. Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/ static void color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) { int bit; for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) { tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); color_tree_init(tree->children[i]); } tree = tree->children[i]; } tree->index = (int)index; } /*put a pixel, given its RGBA color, into image of any color type*/ static unsigned rgba8ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { if(mode->colortype == LCT_GREY) { unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; if(mode->bitdepth == 8) out[i] = grey; else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = grey; else { /*take the most significant bits of grey*/ grey = (grey >> (8 - mode->bitdepth)) & ((1 << mode->bitdepth) - 1); addColorBits(out, i, mode->bitdepth, grey); } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { out[i * 3 + 0] = r; out[i * 3 + 1] = g; out[i * 3 + 2] = b; } else { out[i * 6 + 0] = out[i * 6 + 1] = r; out[i * 6 + 2] = out[i * 6 + 3] = g; out[i * 6 + 4] = out[i * 6 + 5] = b; } } else if(mode->colortype == LCT_PALETTE) { int index = color_tree_get(tree, r, g, b, a); if(index < 0) return 82; /*color not in palette*/ if(mode->bitdepth == 8) out[i] = index; else addColorBits(out, i, mode->bitdepth, (unsigned)index); } else if(mode->colortype == LCT_GREY_ALPHA) { unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; if(mode->bitdepth == 8) { out[i * 2 + 0] = grey; out[i * 2 + 1] = a; } else if(mode->bitdepth == 16) { out[i * 4 + 0] = out[i * 4 + 1] = grey; out[i * 4 + 2] = out[i * 4 + 3] = a; } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { out[i * 4 + 0] = r; out[i * 4 + 1] = g; out[i * 4 + 2] = b; out[i * 4 + 3] = a; } else { out[i * 8 + 0] = out[i * 8 + 1] = r; out[i * 8 + 2] = out[i * 8 + 3] = g; out[i * 8 + 4] = out[i * 8 + 5] = b; out[i * 8 + 6] = out[i * 8 + 7] = a; } } return 0; /*no error*/ } /*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ static void rgba16ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r, unsigned short g, unsigned short b, unsigned short a) { if(mode->colortype == LCT_GREY) { unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; out[i * 2 + 0] = (grey >> 8) & 255; out[i * 2 + 1] = grey & 255; } else if(mode->colortype == LCT_RGB) { out[i * 6 + 0] = (r >> 8) & 255; out[i * 6 + 1] = r & 255; out[i * 6 + 2] = (g >> 8) & 255; out[i * 6 + 3] = g & 255; out[i * 6 + 4] = (b >> 8) & 255; out[i * 6 + 5] = b & 255; } else if(mode->colortype == LCT_GREY_ALPHA) { unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; out[i * 4 + 0] = (grey >> 8) & 255; out[i * 4 + 1] = grey & 255; out[i * 4 + 2] = (a >> 8) & 255; out[i * 4 + 3] = a & 255; } else if(mode->colortype == LCT_RGBA) { out[i * 8 + 0] = (r >> 8) & 255; out[i * 8 + 1] = r & 255; out[i * 8 + 2] = (g >> 8) & 255; out[i * 8 + 3] = g & 255; out[i * 8 + 4] = (b >> 8) & 255; out[i * 8 + 5] = b & 255; out[i * 8 + 6] = (a >> 8) & 255; out[i * 8 + 7] = a & 255; } } /*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) { if(mode->colortype == LCT_GREY) { if(mode->bitdepth == 8) { *r = *g = *b = in[i]; if(mode->key_defined && *r == mode->key_r) *a = 0; else *a = 255; } else if(mode->bitdepth == 16) { *r = *g = *b = in[i * 2 + 0]; if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; else *a = 255; } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = i * mode->bitdepth; unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); *r = *g = *b = (value * 255) / highest; if(mode->key_defined && value == mode->key_r) *a = 0; else *a = 255; } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; else *a = 255; } else { *r = in[i * 6 + 0]; *g = in[i * 6 + 2]; *b = in[i * 6 + 4]; if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 255; } } else if(mode->colortype == LCT_PALETTE) { unsigned index; if(mode->bitdepth == 8) index = in[i]; else { size_t j = i * mode->bitdepth; index = readBitsFromReversedStream(&j, in, mode->bitdepth); } if(index >= mode->palettesize) { /*This is an error according to the PNG spec, but common PNG decoders make it black instead. Done here too, slightly faster due to no error handling needed.*/ *r = *g = *b = 0; *a = 255; } else { *r = mode->palette[index * 4 + 0]; *g = mode->palette[index * 4 + 1]; *b = mode->palette[index * 4 + 2]; *a = mode->palette[index * 4 + 3]; } } else if(mode->colortype == LCT_GREY_ALPHA) { if(mode->bitdepth == 8) { *r = *g = *b = in[i * 2 + 0]; *a = in[i * 2 + 1]; } else { *r = *g = *b = in[i * 4 + 0]; *a = in[i * 4 + 2]; } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { *r = in[i * 4 + 0]; *g = in[i * 4 + 1]; *b = in[i * 4 + 2]; *a = in[i * 4 + 3]; } else { *r = in[i * 8 + 0]; *g = in[i * 8 + 2]; *b = in[i * 8 + 4]; *a = in[i * 8 + 6]; } } } /*Similar to getPixelColorRGBA8, but with all the for loops inside of the color mode test cases, optimized to convert the colors much faster, when converting to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with enough memory, if has_alpha is true the output is RGBA. mode has the color mode of the input buffer.*/ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, unsigned has_alpha, const unsigned char* in, const LodePNGColorMode* mode) { unsigned num_channels = has_alpha ? 4 : 3; size_t i; if(mode->colortype == LCT_GREY) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i]; if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; } } else if(mode->bitdepth == 16) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2]; if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; } } else { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = 0; for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; if(has_alpha) buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; } } } else if(mode->colortype == LCT_RGB) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 3 + 0]; buffer[1] = in[i * 3 + 1]; buffer[2] = in[i * 3 + 2]; if(has_alpha) buffer[3] = mode->key_defined && buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b ? 0 : 255; } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 6 + 0]; buffer[1] = in[i * 6 + 2]; buffer[2] = in[i * 6 + 4]; if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; } } } else if(mode->colortype == LCT_PALETTE) { unsigned index; size_t j = 0; for(i = 0; i != numpixels; ++i, buffer += num_channels) { if(mode->bitdepth == 8) index = in[i]; else index = readBitsFromReversedStream(&j, in, mode->bitdepth); if(index >= mode->palettesize) { /*This is an error according to the PNG spec, but most PNG decoders make it black instead. Done here too, slightly faster due to no error handling needed.*/ buffer[0] = buffer[1] = buffer[2] = 0; if(has_alpha) buffer[3] = 255; } else { buffer[0] = mode->palette[index * 4 + 0]; buffer[1] = mode->palette[index * 4 + 1]; buffer[2] = mode->palette[index * 4 + 2]; if(has_alpha) buffer[3] = mode->palette[index * 4 + 3]; } } } else if(mode->colortype == LCT_GREY_ALPHA) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; if(has_alpha) buffer[3] = in[i * 2 + 1]; } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; if(has_alpha) buffer[3] = in[i * 4 + 2]; } } } else if(mode->colortype == LCT_RGBA) { if(mode->bitdepth == 8) { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 4 + 0]; buffer[1] = in[i * 4 + 1]; buffer[2] = in[i * 4 + 2]; if(has_alpha) buffer[3] = in[i * 4 + 3]; } } else { for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 8 + 0]; buffer[1] = in[i * 8 + 2]; buffer[2] = in[i * 8 + 4]; if(has_alpha) buffer[3] = in[i * 8 + 6]; } } } } /*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with given color type, but the given color type must be 16-bit itself.*/ static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode) { if(mode->colortype == LCT_GREY) { *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; else *a = 65535; } else if(mode->colortype == LCT_RGB) { *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; if(mode->key_defined && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 65535; } else if(mode->colortype == LCT_GREY_ALPHA) { *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; } else if(mode->colortype == LCT_RGBA) { *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; } } unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h) { size_t i; ColorTree tree; size_t numpixels = w * h; if(lodepng_color_mode_equal(mode_out, mode_in)) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); for(i = 0; i != numbytes; ++i) out[i] = in[i]; return 0; } if(mode_out->colortype == LCT_PALETTE) { size_t palettesize = mode_out->palettesize; const unsigned char* palette = mode_out->palette; size_t palsize = 1u << mode_out->bitdepth; /*if the user specified output palette but did not give the values, assume they want the values of the input color type (assuming that one is palette). Note that we never create a new palette ourselves.*/ if(palettesize == 0) { palettesize = mode_in->palettesize; palette = mode_in->palette; } if(palettesize < palsize) palsize = palettesize; color_tree_init(&tree); for(i = 0; i != palsize; ++i) { const unsigned char* p = &palette[i * 4]; color_tree_add(&tree, p[0], p[1], p[2], p[3], i); } } if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { for(i = 0; i != numpixels; ++i) { unsigned short r = 0, g = 0, b = 0, a = 0; getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); rgba16ToPixel(out, i, mode_out, r, g, b, a); } } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) { getPixelColorsRGBA8(out, numpixels, 1, in, mode_in); } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) { getPixelColorsRGBA8(out, numpixels, 0, in, mode_in); } else { unsigned char r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); CERROR_TRY_RETURN(rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a)); } } if(mode_out->colortype == LCT_PALETTE) { color_tree_cleanup(&tree); } return 0; /*no error*/ } #ifdef LODEPNG_COMPILE_ENCODER void lodepng_color_profile_init(LodePNGColorProfile* profile) { profile->colored = 0; profile->key = 0; profile->key_r = profile->key_g = profile->key_b = 0; profile->alpha = 0; profile->numcolors = 0; profile->bits = 1; } /*function used for debug purposes with C++*/ /*void printColorProfile(LodePNGColorProfile* p) { std::cout << "colored: " << (int)p->colored << ", "; std::cout << "key: " << (int)p->key << ", "; std::cout << "key_r: " << (int)p->key_r << ", "; std::cout << "key_g: " << (int)p->key_g << ", "; std::cout << "key_b: " << (int)p->key_b << ", "; std::cout << "alpha: " << (int)p->alpha << ", "; std::cout << "numcolors: " << (int)p->numcolors << ", "; std::cout << "bits: " << (int)p->bits << std::endl; }*/ /*Returns how many bits needed to represent given value (max 8 bit)*/ static unsigned getValueRequiredBits(unsigned char value) { if(value == 0 || value == 255) return 1; /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; return 8; } /*profile must already have been inited with mode. It's ok to set some parameters of profile to done already.*/ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, const unsigned char* in, unsigned w, unsigned h, const LodePNGColorMode* mode) { unsigned error = 0; size_t i; ColorTree tree; size_t numpixels = w * h; unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; unsigned numcolors_done = 0; unsigned bpp = lodepng_get_bpp(mode); unsigned bits_done = bpp == 1 ? 1 : 0; unsigned maxnumcolors = 257; unsigned sixteen = 0; if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); color_tree_init(&tree); /*Check if the 16-bit input is truly 16-bit*/ if(mode->bitdepth == 16) { unsigned short r, g, b, a; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ { sixteen = 1; break; } } } if(sixteen) { unsigned short r = 0, g = 0, b = 0, a = 0; profile->bits = 16; bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); if(!colored_done && (r != g || r != b)) { profile->colored = 1; colored_done = 1; } if(!alpha_done) { unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); if(a != 65535 && (a != 0 || (profile->key && !matchkey))) { profile->alpha = 1; profile->key = 0; alpha_done = 1; } else if(a == 0 && !profile->alpha && !profile->key) { profile->key = 1; profile->key_r = r; profile->key_g = g; profile->key_b = b; } else if(a == 65535 && profile->key && matchkey) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; profile->key = 0; alpha_done = 1; } } if(alpha_done && numcolors_done && colored_done && bits_done) break; } if(profile->key && !profile->alpha) { for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; profile->key = 0; alpha_done = 1; } } } } else /* < 16-bit */ { unsigned char r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); if(!bits_done && profile->bits < 8) { /*only r is checked, < 8 bits is only relevant for greyscale*/ unsigned bits = getValueRequiredBits(r); if(bits > profile->bits) profile->bits = bits; } bits_done = (profile->bits >= bpp); if(!colored_done && (r != g || r != b)) { profile->colored = 1; colored_done = 1; if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ } if(!alpha_done) { unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); if(a != 255 && (a != 0 || (profile->key && !matchkey))) { profile->alpha = 1; profile->key = 0; alpha_done = 1; if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } else if(a == 0 && !profile->alpha && !profile->key) { profile->key = 1; profile->key_r = r; profile->key_g = g; profile->key_b = b; } else if(a == 255 && profile->key && matchkey) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; profile->key = 0; alpha_done = 1; if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } } if(!numcolors_done) { if(!color_tree_has(&tree, r, g, b, a)) { color_tree_add(&tree, r, g, b, a, profile->numcolors); if(profile->numcolors < 256) { unsigned char* p = profile->palette; unsigned n = profile->numcolors; p[n * 4 + 0] = r; p[n * 4 + 1] = g; p[n * 4 + 2] = b; p[n * 4 + 3] = a; } ++profile->numcolors; numcolors_done = profile->numcolors >= maxnumcolors; } } if(alpha_done && numcolors_done && colored_done && bits_done) break; } if(profile->key && !profile->alpha) { for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; profile->key = 0; alpha_done = 1; if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } } } /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ profile->key_r += (profile->key_r << 8); profile->key_g += (profile->key_g << 8); profile->key_b += (profile->key_b << 8); } color_tree_cleanup(&tree); return error; } /*Automatically chooses color type that gives smallest amount of bits in the output image, e.g. grey if there are only greyscale pixels, palette if there are less than 256 colors, ... Updates values of mode with a potentially smaller color model. mode_out should contain the user chosen color model, but will be overwritten with the new chosen one.*/ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, const unsigned char* image, unsigned w, unsigned h, const LodePNGColorMode* mode_in) { LodePNGColorProfile prof; unsigned error = 0; unsigned i, n, palettebits, palette_ok; lodepng_color_profile_init(&prof); error = lodepng_get_color_profile(&prof, image, w, h, mode_in); if(error) return error; mode_out->key_defined = 0; if(prof.key && w * h <= 16) { prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ prof.key = 0; if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } n = prof.numcolors; palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); palette_ok = n <= 256 && prof.bits <= 8; if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ if(palette_ok) { unsigned char* p = prof.palette; lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ for(i = 0; i != prof.numcolors; ++i) { error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); if(error) break; } mode_out->colortype = LCT_PALETTE; mode_out->bitdepth = palettebits; if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize && mode_in->bitdepth == mode_out->bitdepth) { /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ lodepng_color_mode_cleanup(mode_out); lodepng_color_mode_copy(mode_out, mode_in); } } else /*8-bit or 16-bit per channel*/ { mode_out->bitdepth = prof.bits; mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) : (prof.colored ? LCT_RGB : LCT_GREY); if(prof.key) { unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ mode_out->key_r = prof.key_r & mask; mode_out->key_g = prof.key_g & mask; mode_out->key_b = prof.key_b & mask; mode_out->key_defined = 1; } } return error; } #endif /* #ifdef LODEPNG_COMPILE_ENCODER */ /* Paeth predicter, used by PNG filter type 4 The parameters are of type short, but should come from unsigned chars, the shorts are only needed to make the paeth calculation correct. */ static unsigned char paethPredictor(short a, short b, short c) { short pa = abs(b - c); short pb = abs(a - c); short pc = abs(a + b - c - c); if(pc < pa && pc < pb) return (unsigned char)c; else if(pb < pa) return (unsigned char)b; else return (unsigned char)a; } /*shared values used by multiple Adam7 related functions*/ static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ /* Outputs various dimensions and positions in the image related to the Adam7 reduced images. passw: output containing the width of the 7 passes passh: output containing the height of the 7 passes filter_passstart: output containing the index of the start and end of each reduced image with filter bytes padded_passstart output containing the index of the start and end of each reduced image when without filter bytes but with padded scanlines passstart: output containing the index of the start and end of each reduced image without padding between scanlines, but still padding between the images w, h: width and height of non-interlaced image bpp: bits per pixel "padded" is only relevant if bpp is less than 8 and a scanline or image does not end at a full byte */ static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) { /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ unsigned i; /*calculate width and height in pixels of each pass*/ for(i = 0; i != 7; ++i) { passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; if(passw[i] == 0) passh[i] = 0; if(passh[i] == 0) passw[i] = 0; } filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; for(i = 0; i != 7; ++i) { /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ filter_passstart[i + 1] = filter_passstart[i] + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); /*bits padded if needed to fill full byte at end of each scanline*/ padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); /*only padded at end of reduced image*/ passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; } } #ifdef LODEPNG_COMPILE_DECODER /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG Decoder / */ /* ////////////////////////////////////////////////////////////////////////// */ /*read the information from the header and store it in the LodePNGInfo. return value is error*/ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { LodePNGInfo* info = &state->info_png; if(insize == 0 || in == 0) { CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ } if(insize < 33) { CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ } /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ lodepng_info_cleanup(info); lodepng_info_init(info); if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ } if(lodepng_chunk_length(in + 8) != 13) { CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ } if(!lodepng_chunk_type_equals(in + 8, "IHDR")) { CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ } /*read the values given in the header*/ *w = lodepng_read32bitInt(&in[16]); *h = lodepng_read32bitInt(&in[20]); info->color.bitdepth = in[24]; info->color.colortype = (LodePNGColorType)in[25]; info->compression_method = in[26]; info->filter_method = in[27]; info->interlace_method = in[28]; if(*w == 0 || *h == 0) { CERROR_RETURN_ERROR(state->error, 93); } if(!state->decoder.ignore_crc) { unsigned CRC = lodepng_read32bitInt(&in[29]); unsigned checksum = lodepng_crc32(&in[12], 17); if(CRC != checksum) { CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ } } /*error: only compression method 0 is allowed in the specification*/ if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); /*error: only filter method 0 is allowed in the specification*/ if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); /*error: only interlace methods 0 and 1 exist in the specification*/ if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); return state->error; } static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length) { /* For PNG filter method 0 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) precon is the previous unfiltered scanline, recon the result, scanline the current one the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead recon and scanline MAY be the same memory address! precon must be disjoint. */ size_t i; switch(filterType) { case 0: for(i = 0; i != length; ++i) recon[i] = scanline[i]; break; case 1: for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; break; case 2: if(precon) { for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; } else { for(i = 0; i != length; ++i) recon[i] = scanline[i]; } break; case 3: if(precon) { for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); } else { for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); } break; case 4: if(precon) { for(i = 0; i != bytewidth; ++i) { recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ } for(i = bytewidth; i < length; ++i) { recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); } } else { for(i = 0; i != bytewidth; ++i) { recon[i] = scanline[i]; } for(i = bytewidth; i < length; ++i) { /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ recon[i] = (scanline[i] + recon[i - bytewidth]); } } break; default: return 36; /*error: unexisting filter type given*/ } return 0; } static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { /* For PNG filter method 0 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) */ unsigned y; unsigned char* prevline = 0; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ size_t bytewidth = (bpp + 7) / 8; size_t linebytes = (w * bpp + 7) / 8; for(y = 0; y < h; ++y) { size_t outindex = linebytes * y; size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ unsigned char filterType = in[inindex]; CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); prevline = &out[outindex]; } return 0; } /* in: Adam7 interlaced image, with no padding bits between scanlines, but between reduced images so that each reduced image starts at a byte. out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h bpp: bits per pixel out has the following size in bits: w * h * bpp. in is possibly bigger due to padding bits between reduced images. out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation (because that's likely a little bit faster) NOTE: comments about padding bits are only relevant if bpp < 8 */ static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); if(bpp >= 8) { for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8; for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } } } } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ setBitOfReversedStream0(&obp, out, bit); } } } } } static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) { /* After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers for the Adam7 code, the color convert code and the output to the user. in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 only useful if (ilinebits - olinebits) is a value in the range 1..7 */ unsigned y; size_t diff = ilinebits - olinebits; size_t ibp = 0, obp = 0; /*input and output bit pointers*/ for(y = 0; y < h; ++y) { size_t x; for(x = 0; x < olinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } ibp += diff; } } /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks (with filter index bytes and possible padding bits) return value is error*/ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png) { /* This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps: *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace NOTE: the in buffer will be overwritten with intermediate data! */ unsigned bpp = lodepng_get_bpp(&info_png->color); if(bpp == 0) return 31; /*error: invalid colortype*/ if(info_png->interlace_method == 0) { if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) { CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); } /*we can immediately filter into the out buffer, no other steps needed*/ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); } else /*interlace_method is 1 (Adam7)*/ { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); for(i = 0; i != 7; ++i) { CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, move bytes instead of bits or move not at all*/ if(bpp < 8) { /*remove padding bits in scanlines; after this there still may be padding bits between the different reduced images: each reduced image still starts nicely at a byte*/ removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, passh[i]); } } Adam7_deinterlace(out, in, w, h, bpp); } return 0; } static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { unsigned pos = 0, i; if(color->palette) lodepng_free(color->palette); color->palettesize = chunkLength / 3; color->palette = (unsigned char*)lodepng_malloc(4 * color->palettesize); if(!color->palette && color->palettesize) { color->palettesize = 0; return 83; /*alloc fail*/ } if(color->palettesize > 256) return 38; /*error: palette too big*/ for(i = 0; i != color->palettesize; ++i) { color->palette[4 * i + 0] = data[pos++]; /*R*/ color->palette[4 * i + 1] = data[pos++]; /*G*/ color->palette[4 * i + 2] = data[pos++]; /*B*/ color->palette[4 * i + 3] = 255; /*alpha*/ } return 0; /* OK */ } static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { unsigned i; if(color->colortype == LCT_PALETTE) { /*error: more alpha values given than there are palette entries*/ if(chunkLength > color->palettesize) return 38; for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; } else if(color->colortype == LCT_GREY) { /*error: this chunk must be 2 bytes for greyscale image*/ if(chunkLength != 2) return 30; color->key_defined = 1; color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; } else if(color->colortype == LCT_RGB) { /*error: this chunk must be 6 bytes for RGB image*/ if(chunkLength != 6) return 41; color->key_defined = 1; color->key_r = 256u * data[0] + data[1]; color->key_g = 256u * data[2] + data[3]; color->key_b = 256u * data[4] + data[5]; } else return 42; /*error: tRNS chunk not allowed for other color models*/ return 0; /* OK */ } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*background color chunk (bKGD)*/ static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(info->color.colortype == LCT_PALETTE) { /*error: this chunk must be 1 byte for indexed color image*/ if(chunkLength != 1) return 43; info->background_defined = 1; info->background_r = info->background_g = info->background_b = data[0]; } else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { /*error: this chunk must be 2 bytes for greyscale image*/ if(chunkLength != 2) return 44; info->background_defined = 1; info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { /*error: this chunk must be 6 bytes for greyscale image*/ if(chunkLength != 6) return 45; info->background_defined = 1; info->background_r = 256u * data[0] + data[1]; info->background_g = 256u * data[2] + data[3]; info->background_b = 256u * data[4] + data[5]; } return 0; /* OK */ } /*text chunk (tEXt)*/ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { unsigned error = 0; char *key = 0, *str = 0; unsigned i; while(!error) /*not really a while loop, only used to break on error*/ { unsigned length, string2_begin; length = 0; while(length < chunkLength && data[length] != 0) ++length; /*even though it's not allowed by the standard, no error is thrown if there's no null termination char, if the text is empty*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; for(i = 0; i != length; ++i) key[i] = (char)data[i]; string2_begin = length + 1; /*skip keyword null terminator*/ length = chunkLength < string2_begin ? 0 : chunkLength - string2_begin; str = (char*)lodepng_malloc(length + 1); if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ str[length] = 0; for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; error = lodepng_add_text(info, key, str); break; } lodepng_free(key); lodepng_free(str); return error; } /*compressed text chunk (zTXt)*/ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned i; unsigned length, string2_begin; char *key = 0; ucvector decoded; ucvector_init(&decoded); while(!error) /*not really a while loop, only used to break on error*/ { for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; for(i = 0; i != length; ++i) key[i] = (char)data[i]; if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ string2_begin = length + 2; if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ length = chunkLength - string2_begin; /*will fail if zlib error, e.g. if length is too small*/ error = zlib_decompress(&decoded.data, &decoded.size, (unsigned char*)(&data[string2_begin]), length, zlibsettings); if(error) break; ucvector_push_back(&decoded, 0); error = lodepng_add_text(info, key, (char*)decoded.data); break; } lodepng_free(key); ucvector_cleanup(&decoded); return error; } /*international text chunk (iTXt)*/ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, const unsigned char* data, size_t chunkLength) { unsigned error = 0; unsigned i; unsigned length, begin, compressed; char *key = 0, *langtag = 0, *transkey = 0; ucvector decoded; ucvector_init(&decoded); while(!error) /*not really a while loop, only used to break on error*/ { /*Quick check if the chunk length isn't too small. Even without check it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ /*read the key*/ for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ key = (char*)lodepng_malloc(length + 1); if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; for(i = 0; i != length; ++i) key[i] = (char)data[i]; /*read the compression method*/ compressed = data[length + 1]; if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ /*even though it's not allowed by the standard, no error is thrown if there's no null termination char, if the text is empty for the next 3 texts*/ /*read the langtag*/ begin = length + 3; length = 0; for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; langtag = (char*)lodepng_malloc(length + 1); if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ langtag[length] = 0; for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; /*read the transkey*/ begin += length + 1; length = 0; for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; transkey = (char*)lodepng_malloc(length + 1); if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ transkey[length] = 0; for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; /*read the actual text*/ begin += length + 1; length = chunkLength < begin ? 0 : chunkLength - begin; if(compressed) { /*will fail if zlib error, e.g. if length is too small*/ error = zlib_decompress(&decoded.data, &decoded.size, (unsigned char*)(&data[begin]), length, zlibsettings); if(error) break; if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size; ucvector_push_back(&decoded, 0); } else { if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); decoded.data[length] = 0; for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; } error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); break; } lodepng_free(key); lodepng_free(langtag); lodepng_free(transkey); ucvector_cleanup(&decoded); return error; } static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ info->time_defined = 1; info->time.year = 256u * data[0] + data[1]; info->time.month = data[2]; info->time.day = data[3]; info->time.hour = data[4]; info->time.minute = data[5]; info->time.second = data[6]; return 0; /* OK */ } static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ info->phys_defined = 1; info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; info->phys_unit = data[8]; return 0; /* OK */ } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { unsigned char IEND = 0; const unsigned char* chunk; size_t i; ucvector idat; /*the data from idat chunks*/ ucvector scanlines; size_t predict; size_t numpixels; size_t outsize = 0; /*for unknown chunk order*/ unsigned unknown = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*provide some proper output values if error will happen*/ *out = 0; state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ if(state->error) return; numpixels = *w * *h; /*multiplication overflow*/ if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); /*multiplication overflow possible further below. Allows up to 2^31-1 pixel bytes with 16-bit RGBA, the rest is room for filter bytes.*/ if(numpixels > 268435455) CERROR_RETURN(state->error, 92); ucvector_init(&idat); chunk = &in[33]; /*first byte of the first chunk after the header*/ /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer*/ while(!IEND && !state->error) { unsigned chunkLength; const unsigned char* data; /*the data in the chunk*/ /*error: size of the in buffer too small to contain next chunk*/ if((size_t)((chunk - in) + 12) > insize || chunk < in) CERROR_BREAK(state->error, 30); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ chunkLength = lodepng_chunk_length(chunk); /*error: chunk length larger than the max PNG chunk size*/ if(chunkLength > 2147483647) CERROR_BREAK(state->error, 63); if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) { CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ } data = lodepng_chunk_data_const(chunk); /*IDAT chunk, containing compressed image data*/ if(lodepng_chunk_type_equals(chunk, "IDAT")) { size_t oldsize = idat.size; if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 3; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } /*IEND chunk*/ else if(lodepng_chunk_type_equals(chunk, "IEND")) { IEND = 1; } /*palette chunk (PLTE)*/ else if(lodepng_chunk_type_equals(chunk, "PLTE")) { state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); if(state->error) break; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 2; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } /*palette transparency chunk (tRNS)*/ else if(lodepng_chunk_type_equals(chunk, "tRNS")) { state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); if(state->error) break; } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*background color chunk (bKGD)*/ else if(lodepng_chunk_type_equals(chunk, "bKGD")) { state->error = readChunk_bKGD(&state->info_png, data, chunkLength); if(state->error) break; } /*text chunk (tEXt)*/ else if(lodepng_chunk_type_equals(chunk, "tEXt")) { if(state->decoder.read_text_chunks) { state->error = readChunk_tEXt(&state->info_png, data, chunkLength); if(state->error) break; } } /*compressed text chunk (zTXt)*/ else if(lodepng_chunk_type_equals(chunk, "zTXt")) { if(state->decoder.read_text_chunks) { state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); if(state->error) break; } } /*international text chunk (iTXt)*/ else if(lodepng_chunk_type_equals(chunk, "iTXt")) { if(state->decoder.read_text_chunks) { state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); if(state->error) break; } } else if(lodepng_chunk_type_equals(chunk, "tIME")) { state->error = readChunk_tIME(&state->info_png, data, chunkLength); if(state->error) break; } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { state->error = readChunk_pHYs(&state->info_png, data, chunkLength); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ if(!lodepng_chunk_ancillary(chunk)) CERROR_BREAK(state->error, 69); unknown = 1; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS if(state->decoder.remember_unknown_chunks) { state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ { if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ } if(!IEND) chunk = lodepng_chunk_next_const(chunk); } ucvector_init(&scanlines); /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. If the decompressed size does not match the prediction, the image must be corrupt.*/ if(state->info_png.interlace_method == 0) { /*The extra *h is added because this are the filter bytes every scanline starts with*/ predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; } else { /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ const LodePNGColorMode* color = &state->info_png.color; predict = 0; predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); } if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ if(!state->error) { state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, idat.size, &state->decoder.zlibsettings); if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ } ucvector_cleanup(&idat); if(!state->error) { outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); *out = (unsigned char*)lodepng_malloc(outsize); if(!*out) state->error = 83; /*alloc fail*/ } if(!state->error) { for(i = 0; i < outsize; i++) (*out)[i] = 0; state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); } ucvector_cleanup(&scanlines); } unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { *out = 0; decodeGeneric(out, w, h, state, in, insize); if(state->error) return state->error; if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) { /*same color type, no copying or converting of data needed*/ /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype the raw image has to the end user*/ if(!state->decoder.color_convert) { state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); if(state->error) return state->error; } } else { /*color conversion needed; sort of copy of the data*/ unsigned char* data = *out; size_t outsize; /*TODO: check if this works according to the statement in the documentation: "The converter can convert from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/ if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) && !(state->info_raw.bitdepth == 8)) { return 56; /*unsupported color mode conversion*/ } outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); *out = (unsigned char*)lodepng_malloc(outsize); if(!(*out)) { state->error = 83; /*alloc fail*/ } else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h); lodepng_free(data); } return state->error; } unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth) { unsigned error; LodePNGState state; lodepng_state_init(&state); state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; error = lodepng_decode(out, w, h, &state, in, insize); lodepng_state_cleanup(&state); return error; } unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); } unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); } #ifdef LODEPNG_COMPILE_DISK unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer = 0; size_t buffersize; unsigned error; error = lodepng_load_file(&buffer, &buffersize, filename); if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); lodepng_free(buffer); return error; } unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); } unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); } #endif /*LODEPNG_COMPILE_DISK*/ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) { settings->color_convert = 1; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS settings->read_text_chunks = 1; settings->remember_unknown_chunks = 0; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ settings->ignore_crc = 0; lodepng_decompress_settings_init(&settings->zlibsettings); } #endif /*LODEPNG_COMPILE_DECODER*/ #if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) void lodepng_state_init(LodePNGState* state) { #ifdef LODEPNG_COMPILE_DECODER lodepng_decoder_settings_init(&state->decoder); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER lodepng_encoder_settings_init(&state->encoder); #endif /*LODEPNG_COMPILE_ENCODER*/ lodepng_color_mode_init(&state->info_raw); lodepng_info_init(&state->info_png); state->error = 1; } void lodepng_state_cleanup(LodePNGState* state) { lodepng_color_mode_cleanup(&state->info_raw); lodepng_info_cleanup(&state->info_png); } void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) { lodepng_state_cleanup(dest); *dest = *source; lodepng_color_mode_init(&dest->info_raw); lodepng_info_init(&dest->info_png); dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; } #endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ #ifdef LODEPNG_COMPILE_ENCODER /* ////////////////////////////////////////////////////////////////////////// */ /* / PNG Encoder / */ /* ////////////////////////////////////////////////////////////////////////// */ /*chunkName must be string of 4 characters*/ static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) { CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data)); out->allocsize = out->size; /*fix the allocsize again*/ return 0; } static void writeSignature(ucvector* out) { /*8 bytes PNG signature, aka the magic bytes*/ ucvector_push_back(out, 137); ucvector_push_back(out, 80); ucvector_push_back(out, 78); ucvector_push_back(out, 71); ucvector_push_back(out, 13); ucvector_push_back(out, 10); ucvector_push_back(out, 26); ucvector_push_back(out, 10); } static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) { unsigned error = 0; ucvector header; ucvector_init(&header); lodepng_add32bitInt(&header, w); /*width*/ lodepng_add32bitInt(&header, h); /*height*/ ucvector_push_back(&header, (unsigned char)bitdepth); /*bit depth*/ ucvector_push_back(&header, (unsigned char)colortype); /*color type*/ ucvector_push_back(&header, 0); /*compression method*/ ucvector_push_back(&header, 0); /*filter method*/ ucvector_push_back(&header, interlace_method); /*interlace method*/ error = addChunk(out, "IHDR", header.data, header.size); ucvector_cleanup(&header); return error; } static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) { unsigned error = 0; size_t i; ucvector PLTE; ucvector_init(&PLTE); for(i = 0; i != info->palettesize * 4; ++i) { /*add all channels except alpha channel*/ if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); } error = addChunk(out, "PLTE", PLTE.data, PLTE.size); ucvector_cleanup(&PLTE); return error; } static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) { unsigned error = 0; size_t i; ucvector tRNS; ucvector_init(&tRNS); if(info->colortype == LCT_PALETTE) { size_t amount = info->palettesize; /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ for(i = info->palettesize; i != 0; --i) { if(info->palette[4 * (i - 1) + 3] == 255) --amount; else break; } /*add only alpha channel*/ for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); } else if(info->colortype == LCT_GREY) { if(info->key_defined) { ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); } } else if(info->colortype == LCT_RGB) { if(info->key_defined) { ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); } } error = addChunk(out, "tRNS", tRNS.data, tRNS.size); ucvector_cleanup(&tRNS); return error; } static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, LodePNGCompressSettings* zlibsettings) { ucvector zlibdata; unsigned error = 0; /*compress with the Zlib compressor*/ ucvector_init(&zlibdata); error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings); if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size); ucvector_cleanup(&zlibdata); return error; } static unsigned addChunk_IEND(ucvector* out) { unsigned error = 0; error = addChunk(out, "IEND", 0, 0); return error; } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) { unsigned error = 0; size_t i; ucvector text; ucvector_init(&text); for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&text, 0); /*0 termination char*/ for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); error = addChunk(out, "tEXt", text.data, text.size); ucvector_cleanup(&text); return error; } static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; ucvector data, compressed; size_t i, textsize = strlen(textstring); ucvector_init(&data); ucvector_init(&compressed); for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&data, 0); /*0 termination char*/ ucvector_push_back(&data, 0); /*compression method: 0*/ error = zlib_compress(&compressed.data, &compressed.size, (unsigned char*)textstring, textsize, zlibsettings); if(!error) { for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); error = addChunk(out, "zTXt", data.data, data.size); } ucvector_cleanup(&compressed); ucvector_cleanup(&data); return error; } static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag, const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) { unsigned error = 0; ucvector data; size_t i, textsize = strlen(textstring); ucvector_init(&data); for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&data, 0); /*null termination char*/ ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ ucvector_push_back(&data, 0); /*compression method*/ for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); ucvector_push_back(&data, 0); /*null termination char*/ for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); ucvector_push_back(&data, 0); /*null termination char*/ if(compressed) { ucvector compressed_data; ucvector_init(&compressed_data); error = zlib_compress(&compressed_data.data, &compressed_data.size, (unsigned char*)textstring, textsize, zlibsettings); if(!error) { for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); } ucvector_cleanup(&compressed_data); } else /*not compressed*/ { for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); } if(!error) error = addChunk(out, "iTXt", data.data, data.size); ucvector_cleanup(&data); return error; } static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) { unsigned error = 0; ucvector bKGD; ucvector_init(&bKGD); if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); } else if(info->color.colortype == LCT_PALETTE) { ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ } error = addChunk(out, "bKGD", bKGD.data, bKGD.size); ucvector_cleanup(&bKGD); return error; } static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) { unsigned error = 0; unsigned char* data = (unsigned char*)lodepng_malloc(7); if(!data) return 83; /*alloc fail*/ data[0] = (unsigned char)(time->year >> 8); data[1] = (unsigned char)(time->year & 255); data[2] = (unsigned char)time->month; data[3] = (unsigned char)time->day; data[4] = (unsigned char)time->hour; data[5] = (unsigned char)time->minute; data[6] = (unsigned char)time->second; error = addChunk(out, "tIME", data, 7); lodepng_free(data); return error; } static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) { unsigned error = 0; ucvector data; ucvector_init(&data); lodepng_add32bitInt(&data, info->phys_x); lodepng_add32bitInt(&data, info->phys_y); ucvector_push_back(&data, info->phys_unit); error = addChunk(out, "pHYs", data.data, data.size); ucvector_cleanup(&data); return error; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, size_t length, size_t bytewidth, unsigned char filterType) { size_t i; switch(filterType) { case 0: /*None*/ for(i = 0; i != length; ++i) out[i] = scanline[i]; break; case 1: /*Sub*/ for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; break; case 2: /*Up*/ if(prevline) { for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; } else { for(i = 0; i != length; ++i) out[i] = scanline[i]; } break; case 3: /*Average*/ if(prevline) { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); } else { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); } break; case 4: /*Paeth*/ if(prevline) { /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); for(i = bytewidth; i < length; ++i) { out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); } } else { for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); } break; default: return; /*unexisting filter type given*/ } } /* log2 approximation. A slight bit faster than std::log. */ static float flog2(float f) { float result = 0; while(f > 32) { result += 4; f /= 16; } while(f > 2) { ++result; f /= 2; } return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); } static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) { /* For PNG filter method 0 out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are the scanlines with 1 extra byte per scanline */ unsigned bpp = lodepng_get_bpp(info); /*the width of a scanline in bytes, not including the filter type*/ size_t linebytes = (w * bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ size_t bytewidth = (bpp + 7) / 8; const unsigned char* prevline = 0; unsigned x, y; unsigned error = 0; LodePNGFilterStrategy strategy = settings->filter_strategy; /* There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. use fixed filtering, with the filter None). * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply all five filters and select the filter that produces the smallest sum of absolute values per row. This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum heuristic is used. */ if(settings->filter_palette_zero && (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO; if(bpp == 0) return 31; /*error: invalid color type*/ if(strategy == LFS_ZERO) { for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; out[outindex] = 0; /*filter type byte*/ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, 0); prevline = &in[inindex]; } } else if(strategy == LFS_MINSUM) { /*adaptive filtering*/ size_t sum[5]; unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned char type, bestType = 0; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) return 83; /*alloc fail*/ } if(!error) { for(y = 0; y != h; ++y) { /*try the 5 filter types*/ for(type = 0; type != 5; ++type) { filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); /*calculate the sum of the result*/ sum[type] = 0; if(type == 0) { for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); } else { for(x = 0; x != linebytes; ++x) { /*For differences, each byte should be treated as signed, values above 127 are negative (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. This means filtertype 0 is almost never chosen, but that is justified.*/ unsigned char s = attempt[type][x]; sum[type] += s < 128 ? s : (255U - s); } } /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || sum[type] < smallest) { bestType = type; smallest = sum[type]; } } prevline = &in[y * linebytes]; /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_ENTROPY) { float sum[5]; unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ float smallest = 0; unsigned type, bestType = 0; unsigned count[256]; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) return 83; /*alloc fail*/ } for(y = 0; y != h; ++y) { /*try the 5 filter types*/ for(type = 0; type != 5; ++type) { filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); for(x = 0; x != 256; ++x) count[x] = 0; for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; ++count[type]; /*the filter type itself is part of the scanline*/ sum[type] = 0; for(x = 0; x != 256; ++x) { float p = count[x] / (float)(linebytes + 1); sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; } /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || sum[type] < smallest) { bestType = type; smallest = sum[type]; } } prevline = &in[y * linebytes]; /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_PREDEFINED) { for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; unsigned char type = settings->predefined_filters[y]; out[outindex] = type; /*filter type byte*/ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); prevline = &in[inindex]; } } else if(strategy == LFS_BRUTE_FORCE) { /*brute force filter chooser. deflate the scanline after every filter attempt to see which one deflates best. This is very slow and gives only slightly smaller, sometimes even larger, result*/ size_t size[5]; unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned type = 0, bestType = 0; unsigned char* dummy; LodePNGCompressSettings zlibsettings = settings->zlibsettings; /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, to simulate the true case where the tree is the same for the whole image. Sometimes it gives better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare cases better compression. It does make this a bit less slow, so it's worth doing this.*/ zlibsettings.btype = 1; /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG images only, so disable it*/ zlibsettings.custom_zlib = 0; zlibsettings.custom_deflate = 0; for(type = 0; type != 5; ++type) { attempt[type] = (unsigned char*)lodepng_malloc(linebytes); if(!attempt[type]) return 83; /*alloc fail*/ } for(y = 0; y != h; ++y) /*try the 5 filter types*/ { for(type = 0; type != 5; ++type) { unsigned testsize = linebytes; /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); size[type] = 0; dummy = 0; zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); lodepng_free(dummy); /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || size[type] < smallest) { bestType = type; smallest = size[type]; } } prevline = &in[y * linebytes]; out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else return 88; /* unknown filter strategy */ return error; } static void addPaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h) { /*The opposite of the removePaddingBits function olinebits must be >= ilinebits*/ unsigned y; size_t diff = olinebits - ilinebits; size_t obp = 0, ibp = 0; /*bit pointers*/ for(y = 0; y != h; ++y) { size_t x; for(x = 0; x < ilinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } /*obp += diff; --> no, fill in some value in the padding bits too, to avoid "Use of uninitialised value of size ###" warning from valgrind*/ for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); } } /* in: non-interlaced image with size w*h out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with no padding bits between scanlines, but between reduced images so that each reduced image starts at a byte. bpp: bits per pixel there are no padding bits, not between scanlines, not between reduced images in has the following size in bits: w * h * bpp. out is possibly bigger due to padding bits between reduced images NOTE: comments about padding bits are only relevant if bpp < 8 */ static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned i; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); if(bpp >= 8) { for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8; for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } } } } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ for(y = 0; y < passh[i]; ++y) for(x = 0; x < passw[i]; ++x) { ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } } } } } /*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. return value is error**/ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) { /* This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: *) if no Adam7: 1) add padding bits (= posible extra bits per scanline if bpp < 8) 2) filter *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter */ unsigned bpp = lodepng_get_bpp(&info_png->color); unsigned error = 0; if(info_png->interlace_method == 0) { *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/ *out = (unsigned char*)lodepng_malloc(*outsize); if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ if(!error) { /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) { unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7) / 8)); if(!padded) error = 83; /*alloc fail*/ if(!error) { addPaddingBits(padded, in, ((w * bpp + 7) / 8) * 8, w * bpp, h); error = filter(*out, padded, w, h, &info_png->color, settings); } lodepng_free(padded); } else { /*we can immediately filter into the out buffer, no other steps needed*/ error = filter(*out, in, w, h, &info_png->color, settings); } } } else /*interlace_method is 1 (Adam7)*/ { unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; unsigned char* adam7; Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ *out = (unsigned char*)lodepng_malloc(*outsize); if(!(*out)) error = 83; /*alloc fail*/ adam7 = (unsigned char*)lodepng_malloc(passstart[7]); if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ if(!error) { unsigned i; Adam7_interlace(adam7, in, w, h, bpp); for(i = 0; i != 7; ++i) { if(bpp < 8) { unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); if(!padded) ERROR_BREAK(83); /*alloc fail*/ addPaddingBits(padded, &adam7[passstart[i]], ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]); error = filter(&(*out)[filter_passstart[i]], padded, passw[i], passh[i], &info_png->color, settings); lodepng_free(padded); } else { error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], passw[i], passh[i], &info_png->color, settings); } if(error) break; } } lodepng_free(adam7); } return error; } /* palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA... returns 0 if the palette is opaque, returns 1 if the palette has a single color with alpha 0 ==> color key returns 2 if the palette is semi-translucent. */ static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) { size_t i; unsigned key = 0; unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ for(i = 0; i != palettesize; ++i) { if(!key && palette[4 * i + 3] == 0) { r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2]; key = 1; i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/ } else if(palette[4 * i + 3] != 255) return 2; /*when key, no opaque RGB may have key's RGB*/ else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2; } return key; } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) { unsigned char* inchunk = data; while((size_t)(inchunk - data) < datasize) { CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); out->allocsize = out->size; /*fix the allocsize again*/ inchunk = lodepng_chunk_next(inchunk); } return 0; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ unsigned lodepng_encode(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGState* state) { LodePNGInfo info; ucvector outv; unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ size_t datasize = 0; /*provide some proper output values if error will happen*/ *out = 0; *outsize = 0; state->error = 0; lodepng_info_init(&info); lodepng_info_copy(&info, &state->info_png); if((info.color.colortype == LCT_PALETTE || state->encoder.force_palette) && (info.color.palettesize == 0 || info.color.palettesize > 256)) { state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/ return state->error; } if(state->encoder.auto_convert) { state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); } if(state->error) return state->error; if(state->encoder.zlibsettings.btype > 2) { CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ } if(state->info_png.interlace_method > 1) { CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ } state->error = checkColorValidity(info.color.colortype, info.color.bitdepth); if(state->error) return state->error; /*error: unexisting color type given*/ state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); if(state->error) return state->error; /*error: unexisting color type given*/ if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) { unsigned char* converted; size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; converted = (unsigned char*)lodepng_malloc(size); if(!converted && size) state->error = 83; /*alloc fail*/ if(!state->error) { state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); } if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); lodepng_free(converted); } else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); ucvector_init(&outv); while(!state->error) /*while only executed once, to break on error*/ { #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS size_t i; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*write signature and chunks*/ writeSignature(&outv); /*IHDR*/ addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*unknown chunks between IHDR and PLTE*/ if(info.unknown_chunks_data[0]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*PLTE*/ if(info.color.colortype == LCT_PALETTE) { addChunk_PLTE(&outv, &info.color); } if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) { addChunk_PLTE(&outv, &info.color); } /*tRNS*/ if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) { addChunk_tRNS(&outv, &info.color); } if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) { addChunk_tRNS(&outv, &info.color); } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*bKGD (must come between PLTE and the IDAt chunks*/ if(info.background_defined) addChunk_bKGD(&outv, &info); /*pHYs (must come before the IDAT chunks)*/ if(info.phys_defined) addChunk_pHYs(&outv, &info); /*unknown chunks between PLTE and IDAT*/ if(info.unknown_chunks_data[1]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*IDAT (multiple IDAT chunks must be consecutive)*/ state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); if(state->error) break; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*tIME*/ if(info.time_defined) addChunk_tIME(&outv, &info.time); /*tEXt and/or zTXt*/ for(i = 0; i != info.text_num; ++i) { if(strlen(info.text_keys[i]) > 79) { state->error = 66; /*text chunk too large*/ break; } if(strlen(info.text_keys[i]) < 1) { state->error = 67; /*text chunk too small*/ break; } if(state->encoder.text_compression) { addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); } else { addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); } } /*LodePNG version id in text chunk*/ if(state->encoder.add_id) { unsigned alread_added_id_text = 0; for(i = 0; i != info.text_num; ++i) { if(!strcmp(info.text_keys[i], "LodePNG")) { alread_added_id_text = 1; break; } } if(alread_added_id_text == 0) { addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ } } /*iTXt*/ for(i = 0; i != info.itext_num; ++i) { if(strlen(info.itext_keys[i]) > 79) { state->error = 66; /*text chunk too large*/ break; } if(strlen(info.itext_keys[i]) < 1) { state->error = 67; /*text chunk too small*/ break; } addChunk_iTXt(&outv, state->encoder.text_compression, info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], &state->encoder.zlibsettings); } /*unknown chunks between IDAT and IEND*/ if(info.unknown_chunks_data[2]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); if(state->error) break; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ addChunk_IEND(&outv); break; /*this isn't really a while loop; no error happened so break out now!*/ } lodepng_info_cleanup(&info); lodepng_free(data); /*instead of cleaning the vector up, give it to the output*/ *out = outv.data; *outsize = outv.size; return state->error; } unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned error; LodePNGState state; lodepng_state_init(&state); state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; state.info_png.color.colortype = colortype; state.info_png.color.bitdepth = bitdepth; lodepng_encode(out, outsize, image, w, h, &state); error = state.error; lodepng_state_cleanup(&state); return error; } unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); } unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); } #ifdef LODEPNG_COMPILE_DISK unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); if(!error) error = lodepng_save_file(buffer, buffersize, filename); lodepng_free(buffer); return error; } unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); } unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); } #endif /*LODEPNG_COMPILE_DISK*/ void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) { lodepng_compress_settings_init(&settings->zlibsettings); settings->filter_palette_zero = 1; settings->filter_strategy = LFS_MINSUM; settings->auto_convert = 1; settings->force_palette = 0; settings->predefined_filters = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS settings->add_id = 0; settings->text_compression = 1; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ERROR_TEXT /* This returns the description of a numerical error code in English. This is also the documentation of all the error codes. */ const char* lodepng_error_text(unsigned code) { switch(code) { case 0: return "no error, everything went ok"; case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ case 13: return "problem while processing dynamic deflate block"; case 14: return "problem while processing dynamic deflate block"; case 15: return "problem while processing dynamic deflate block"; case 16: return "unexisting code while processing dynamic deflate block"; case 17: return "end of out buffer memory reached while inflating"; case 18: return "invalid distance code while inflating"; case 19: return "end of out buffer memory reached while inflating"; case 20: return "invalid deflate block BTYPE encountered while decoding"; case 21: return "NLEN is not ones complement of LEN in a deflate block"; /*end of out buffer memory reached while inflating: This can happen if the inflated deflate data is longer than the amount of bytes required to fill up all the pixels of the image, given the color depth and image dimensions. Something that doesn't happen in a normal, well encoded, PNG image.*/ case 22: return "end of out buffer memory reached while inflating"; case 23: return "end of in buffer memory reached while inflating"; case 24: return "invalid FCHECK in zlib header"; case 25: return "invalid compression method in zlib header"; case 26: return "FDICT encountered in zlib header while it's not used for PNG"; case 27: return "PNG file is smaller than a PNG header"; /*Checks the magic file header, the first 8 bytes of the PNG file*/ case 28: return "incorrect PNG signature, it's no PNG or corrupted"; case 29: return "first chunk is not the header chunk"; case 30: return "chunk length too large, chunk broken off at end of file"; case 31: return "illegal PNG color type or bpp"; case 32: return "illegal PNG compression method"; case 33: return "illegal PNG filter method"; case 34: return "illegal PNG interlace method"; case 35: return "chunk length of a chunk is too large or the chunk too small"; case 36: return "illegal PNG filter type encountered"; case 37: return "illegal bit depth for this color type given"; case 38: return "the palette is too big"; /*more than 256 colors*/ case 39: return "more palette alpha values given in tRNS chunk than there are colors in the palette"; case 40: return "tRNS chunk has wrong size for greyscale image"; case 41: return "tRNS chunk has wrong size for RGB image"; case 42: return "tRNS chunk appeared while it was not allowed for this color type"; case 43: return "bKGD chunk has wrong size for palette image"; case 44: return "bKGD chunk has wrong size for greyscale image"; case 45: return "bKGD chunk has wrong size for RGB image"; case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; case 49: return "jumped past memory while generating dynamic huffman tree"; case 50: return "jumped past memory while generating dynamic huffman tree"; case 51: return "jumped past memory while inflating huffman block"; case 52: return "jumped past memory while inflating"; case 53: return "size of zlib data too small"; case 54: return "repeat symbol in tree while there was no value symbol yet"; /*jumped past tree while generating huffman tree, this could be when the tree will have more leaves than symbols after generating it out of the given lenghts. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ case 55: return "jumped past tree while generating huffman tree"; case 56: return "given output image colortype or bitdepth not supported for color conversion"; case 57: return "invalid CRC encountered (checking CRC can be disabled)"; case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; case 59: return "requested color conversion not supported"; case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; /*LodePNG leaves the choice of RGB to greyscale conversion formula to the user.*/ case 62: return "conversion from color to greyscale not supported"; case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; /*(2^31-1)*/ /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; case 71: return "unexisting interlace mode given to encoder (must be 0 or 1)"; case 72: return "while decoding, unexisting compression method encountering in zTXt or iTXt chunk (it must be 0)"; case 73: return "invalid tIME chunk size"; case 74: return "invalid pHYs chunk size"; /*length could be wrong, or data chopped off*/ case 75: return "no null termination char found while decoding text chunk"; case 76: return "iTXt chunk too short to contain required bytes"; case 77: return "integer overflow in buffer size"; case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ case 79: return "failed to open file for writing"; case 80: return "tried creating a tree of 0 symbols"; case 81: return "lazy matching at pos 0 is impossible"; case 82: return "color conversion to palette requested while a color isn't in palette"; case 83: return "memory allocation failed"; case 84: return "given image too small to contain all pixels to be encoded"; case 86: return "impossible offset in lz77 encoding (internal bug)"; case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; case 89: return "text chunk keyword too short or long: must have size 1-79"; /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ case 90: return "windowsize must be a power of two"; case 91: return "invalid decompressed idat size"; case 92: return "too many pixels, not supported"; case 93: return "zero width or height is invalid"; case 94: return "header chunk must have a size of 13 bytes"; } return "unknown error code"; } #endif /*LODEPNG_COMPILE_ERROR_TEXT*/ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ /* // C++ Wrapper // */ /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ #ifdef LODEPNG_COMPILE_CPP namespace lodepng { #ifdef LODEPNG_COMPILE_DISK unsigned load_file(std::vector& buffer, const std::string& filename) { long size = lodepng_filesize(filename.c_str()); if(size < 0) return 78; buffer.resize((size_t)size); return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ unsigned save_file(const std::vector& buffer, const std::string& filename) { return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); } #endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings& settings) { unsigned char* buffer = 0; size_t buffersize = 0; unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned decompress(std::vector& out, const std::vector& in, const LodePNGDecompressSettings& settings) { return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); } #endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER unsigned compress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGCompressSettings& settings) { unsigned char* buffer = 0; size_t buffersize = 0; unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned compress(std::vector& out, const std::vector& in, const LodePNGCompressSettings& settings) { return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); } #endif /* LODEPNG_COMPILE_ENCODER */ #endif /* LODEPNG_COMPILE_ZLIB */ #ifdef LODEPNG_COMPILE_PNG State::State() { lodepng_state_init(this); } State::State(const State& other) { lodepng_state_init(this); lodepng_state_copy(this, &other); } State::~State() { lodepng_state_cleanup(this); } State& State::operator=(const State& other) { lodepng_state_copy(this, &other); return *this; } #ifdef LODEPNG_COMPILE_DECODER unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer; unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); if(buffer && !error) { State state; state.info_raw.colortype = colortype; state.info_raw.bitdepth = bitdepth; size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::vector& in, LodePNGColorType colortype, unsigned bitdepth) { return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); } unsigned decode(std::vector& out, unsigned& w, unsigned& h, State& state, const unsigned char* in, size_t insize) { unsigned char* buffer = NULL; unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); if(buffer && !error) { size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); out.insert(out.end(), &buffer[0], &buffer[buffersize]); } lodepng_free(buffer); return error; } unsigned decode(std::vector& out, unsigned& w, unsigned& h, State& state, const std::vector& in) { return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); } #ifdef LODEPNG_COMPILE_DISK unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, LodePNGColorType colortype, unsigned bitdepth) { std::vector buffer; unsigned error = load_file(buffer, filename); if(error) return error; return decode(out, w, h, buffer, colortype, bitdepth); } #endif /* LODEPNG_COMPILE_DECODER */ #endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ENCODER unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned encode(std::vector& out, const std::vector& in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); } unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, State& state) { unsigned char* buffer; size_t buffersize; unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); if(buffer) { out.insert(out.end(), &buffer[0], &buffer[buffersize]); lodepng_free(buffer); } return error; } unsigned encode(std::vector& out, const std::vector& in, unsigned w, unsigned h, State& state) { if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; return encode(out, in.empty() ? 0 : &in[0], w, h, state); } #ifdef LODEPNG_COMPILE_DISK unsigned encode(const std::string& filename, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { std::vector buffer; unsigned error = encode(buffer, in, w, h, colortype, bitdepth); if(!error) error = save_file(buffer, filename); return error; } unsigned encode(const std::string& filename, const std::vector& in, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); } #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_ENCODER */ #endif /* LODEPNG_COMPILE_PNG */ } /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ CubicSDR-0.2.3/external/lodepng/lodepng.h000066400000000000000000002405161322677621400202070ustar00rootroot00000000000000/* LodePNG version 20161127 Copyright (c) 2005-2016 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef LODEPNG_H #define LODEPNG_H #include /*for size_t*/ extern const char* LODEPNG_VERSION_STRING; /* The following #defines are used to create code sections. They can be disabled to disable code sections, which can give faster compile time and smaller binary. The "NO_COMPILE" defines are designed to be used to pass as defines to the compiler command to disable them without modifying this header, e.g. -DLODEPNG_NO_COMPILE_ZLIB for gcc. In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to allow implementing a custom lodepng_crc32. */ /*deflate & zlib. If disabled, you must specify alternative zlib functions in the custom_zlib field of the compress and decompress settings*/ #ifndef LODEPNG_NO_COMPILE_ZLIB #define LODEPNG_COMPILE_ZLIB #endif /*png encoder and png decoder*/ #ifndef LODEPNG_NO_COMPILE_PNG #define LODEPNG_COMPILE_PNG #endif /*deflate&zlib decoder and png decoder*/ #ifndef LODEPNG_NO_COMPILE_DECODER #define LODEPNG_COMPILE_DECODER #endif /*deflate&zlib encoder and png encoder*/ #ifndef LODEPNG_NO_COMPILE_ENCODER #define LODEPNG_COMPILE_ENCODER #endif /*the optional built in harddisk file loading and saving functions*/ #ifndef LODEPNG_NO_COMPILE_DISK #define LODEPNG_COMPILE_DISK #endif /*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ #ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS #define LODEPNG_COMPILE_ANCILLARY_CHUNKS #endif /*ability to convert error numerical codes to English text string*/ #ifndef LODEPNG_NO_COMPILE_ERROR_TEXT #define LODEPNG_COMPILE_ERROR_TEXT #endif /*Compile the default allocators (C's free, malloc and realloc). If you disable this, you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your source files with custom allocators.*/ #ifndef LODEPNG_NO_COMPILE_ALLOCATORS #define LODEPNG_COMPILE_ALLOCATORS #endif /*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ #ifdef __cplusplus #ifndef LODEPNG_NO_COMPILE_CPP #define LODEPNG_COMPILE_CPP #endif #endif #ifdef LODEPNG_COMPILE_CPP #include #include #endif /*LODEPNG_COMPILE_CPP*/ #ifdef LODEPNG_COMPILE_PNG /*The PNG color types (also used for raw).*/ typedef enum LodePNGColorType { LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/ LCT_RGB = 2, /*RGB: 8,16 bit*/ LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/ LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/ } LodePNGColorType; #ifdef LODEPNG_COMPILE_DECODER /* Converts PNG data in memory to raw pixel data. out: Output parameter. Pointer to buffer that will contain the raw pixel data. After decoding, its size is w * h * (bytes per pixel) bytes larger than initially. Bytes per pixel depends on colortype and bitdepth. Must be freed after usage with free(*out). Note: for 16-bit per channel colors, uses big endian format like PNG does. w: Output parameter. Pointer to width of pixel data. h: Output parameter. Pointer to height of pixel data. in: Memory buffer with the PNG file. insize: size of the in buffer. colortype: the desired color type for the raw output image. See explanation on PNG color types. bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. Return value: LodePNG error code (0 means no error). */ unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize, LodePNGColorType colortype, unsigned bitdepth); /*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize); /*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize); #ifdef LODEPNG_COMPILE_DISK /* Load PNG from disk, from file with given name. Same as the other decode functions, but instead takes a filename as input. */ unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, LodePNGColorType colortype, unsigned bitdepth); /*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename); /*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename); #endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* Converts raw pixel data into a PNG image in memory. The colortype and bitdepth of the output PNG image cannot be chosen, they are automatically determined by the colortype, bitdepth and content of the input pixel data. Note: for 16-bit per channel colors, needs big endian format like PNG does. out: Output parameter. Pointer to buffer that will contain the PNG image data. Must be freed after usage with free(*out). outsize: Output parameter. Pointer to the size in bytes of the out buffer. image: The raw pixel data to encode. The size of this buffer should be w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. w: width of the raw pixel data in pixels. h: height of the raw pixel data in pixels. colortype: the color type of the raw input image. See explanation on PNG color types. bitdepth: the bit depth of the raw input image. See explanation on PNG color types. Return value: LodePNG error code (0 means no error). */ unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth); /*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h); /*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h); #ifdef LODEPNG_COMPILE_DISK /* Converts raw pixel data into a PNG file on disk. Same as the other encode functions, but instead takes a filename as output. NOTE: This overwrites existing files without warning! */ unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth); /*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h); /*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h); #endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_CPP namespace lodepng { #ifdef LODEPNG_COMPILE_DECODER /*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, size_t insize, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::vector& in, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); #ifdef LODEPNG_COMPILE_DISK /* Converts PNG file from disk to raw pixel data in memory. Same as the other decode functions, but instead takes a filename as input. */ unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER /*Same as lodepng_encode_memory, but encodes to an std::vector. colortype is that of the raw input data. The output PNG color type will be auto chosen.*/ unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); unsigned encode(std::vector& out, const std::vector& in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); #ifdef LODEPNG_COMPILE_DISK /* Converts 32-bit RGBA raw pixel data into a PNG file on disk. Same as the other encode functions, but instead takes a filename as output. NOTE: This overwrites existing files without warning! */ unsigned encode(const std::string& filename, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); unsigned encode(const std::string& filename, const std::vector& in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_ENCODER */ } /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ERROR_TEXT /*Returns an English description of the numerical error code.*/ const char* lodepng_error_text(unsigned code); #endif /*LODEPNG_COMPILE_ERROR_TEXT*/ #ifdef LODEPNG_COMPILE_DECODER /*Settings for zlib decompression*/ typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; struct LodePNGDecompressSettings { unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ /*use custom zlib decoder instead of built in one (default: null)*/ unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); /*use custom deflate decoder instead of built in one (default: null) if custom_zlib is used, custom_deflate is ignored since only the built in zlib function will call custom_deflate*/ unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*); const void* custom_context; /*optional custom settings for custom functions*/ }; extern const LodePNGDecompressSettings lodepng_default_decompress_settings; void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* Settings for zlib compression. Tweaking these settings tweaks the balance between speed and compression ratio. */ typedef struct LodePNGCompressSettings LodePNGCompressSettings; struct LodePNGCompressSettings /*deflate = compress*/ { /*LZ77 related settings*/ unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ /*use custom zlib encoder instead of built in one (default: null)*/ unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGCompressSettings*); /*use custom deflate encoder instead of built in one (default: null) if custom_zlib is used, custom_deflate is ignored since only the built in zlib function will call custom_deflate*/ unsigned (*custom_deflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGCompressSettings*); const void* custom_context; /*optional custom settings for custom functions*/ }; extern const LodePNGCompressSettings lodepng_default_compress_settings; void lodepng_compress_settings_init(LodePNGCompressSettings* settings); #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_PNG /* Color mode of an image. Contains all information required to decode the pixel bits to RGBA colors. This information is the same as used in the PNG file format, and is used both for PNG and raw image data in LodePNG. */ typedef struct LodePNGColorMode { /*header (IHDR)*/ LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ /* palette (PLTE and tRNS) Dynamically allocated with the colors of the palette, including alpha. When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use lodepng_palette_clear, then for each color use lodepng_palette_add. If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette. When decoding, by default you can ignore this palette, since LodePNG already fills the palette colors in the pixels of the raw RGBA output. The palette is only supported for color type 3. */ unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/ size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ /* transparent color key (tRNS) This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. For greyscale PNGs, r, g and b will all 3 be set to the same. When decoding, by default you can ignore this information, since LodePNG sets pixels with this key to transparent already in the raw RGBA output. The color key is only supported for color types 0 and 2. */ unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ unsigned key_r; /*red/greyscale component of color key*/ unsigned key_g; /*green component of color key*/ unsigned key_b; /*blue component of color key*/ } LodePNGColorMode; /*init, cleanup and copy functions to use with this struct*/ void lodepng_color_mode_init(LodePNGColorMode* info); void lodepng_color_mode_cleanup(LodePNGColorMode* info); /*return value is error code (0 means no error)*/ unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); void lodepng_palette_clear(LodePNGColorMode* info); /*add 1 color to the palette*/ unsigned lodepng_palette_add(LodePNGColorMode* info, unsigned char r, unsigned char g, unsigned char b, unsigned char a); /*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ unsigned lodepng_get_bpp(const LodePNGColorMode* info); /*get the amount of color channels used, based on colortype in the struct. If a palette is used, it counts as 1 channel.*/ unsigned lodepng_get_channels(const LodePNGColorMode* info); /*is it a greyscale type? (only colortype 0 or 4)*/ unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); /*has it got an alpha channel? (only colortype 2 or 6)*/ unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); /*has it got a palette? (only colortype 3)*/ unsigned lodepng_is_palette_type(const LodePNGColorMode* info); /*only returns true if there is a palette and there is a value in the palette with alpha < 255. Loops through the palette to check this.*/ unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); /* Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). Returns false if the image can only have opaque pixels. In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, or if "key_defined" is true. */ unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); /*Returns the byte size of a raw image buffer with given width, height and color mode*/ size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*The information of a Time chunk in PNG.*/ typedef struct LodePNGTime { unsigned year; /*2 bytes used (0-65535)*/ unsigned month; /*1-12*/ unsigned day; /*1-31*/ unsigned hour; /*0-23*/ unsigned minute; /*0-59*/ unsigned second; /*0-60 (to allow for leap seconds)*/ } LodePNGTime; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*Information about the PNG image, except pixels, width and height.*/ typedef struct LodePNGInfo { /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ unsigned compression_method;/*compression method of the original file. Always 0.*/ unsigned filter_method; /*filter method of the original file*/ unsigned interlace_method; /*interlace method of the original file*/ LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /* suggested background color chunk (bKGD) This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit. For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding the encoder writes the red one. For palette PNGs: When decoding, the RGB value will be stored, not a palette index. But when encoding, specify the index of the palette in background_r, the other two are then ignored. The decoder does not use this background color to edit the color of pixels. */ unsigned background_defined; /*is a suggested background color given?*/ unsigned background_r; /*red component of suggested background color*/ unsigned background_g; /*green component of suggested background color*/ unsigned background_b; /*blue component of suggested background color*/ /* non-international text chunks (tEXt and zTXt) The char** arrays each contain num strings. The actual messages are in text_strings, while text_keys are keywords that give a short description what the actual text represents, e.g. Title, Author, Description, or anything else. A keyword is minimum 1 character and maximum 79 characters long. It's discouraged to use a single line length longer than 79 characters for texts. Don't allocate these text buffers yourself. Use the init/cleanup functions correctly and use lodepng_add_text and lodepng_clear_text. */ size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ char** text_strings; /*the actual text*/ /* international text chunks (iTXt) Similar to the non-international text chunks, but with additional strings "langtags" and "transkeys". */ size_t itext_num; /*the amount of international texts in this PNG*/ char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ char** itext_strings; /*the actual international text - UTF-8 string*/ /*time chunk (tIME)*/ unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ LodePNGTime time; /*phys chunk (pHYs)*/ unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ unsigned phys_x; /*pixels per unit in x direction*/ unsigned phys_y; /*pixels per unit in y direction*/ unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ /* unknown chunks There are 3 buffers, one for each position in the PNG where unknown chunks can appear each buffer contains all unknown chunks for that position consecutively The 3 buffers are the unknown chunks between certain critical chunks: 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND Do not allocate or traverse this data yourself. Use the chunk traversing functions declared later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. */ unsigned char* unknown_chunks_data[3]; size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } LodePNGInfo; /*init, cleanup and copy functions to use with this struct*/ void lodepng_info_init(LodePNGInfo* info); void lodepng_info_cleanup(LodePNGInfo* info); /*return value is error code (0 means no error)*/ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /* Converts raw buffer from one color type to another color type, based on LodePNGColorMode structs to describe the input and output color type. See the reference manual at the end of this header file to see which color conversions are supported. return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel of the output color type (lodepng_get_bpp). For < 8 bpp images, there should not be padding bits at the end of scanlines. For 16-bit per channel colors, uses big endian format like PNG does. Return value is LodePNG error code */ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h); #ifdef LODEPNG_COMPILE_DECODER /* Settings for the decoder. This contains settings for the PNG and the Zlib decoder, but not the Info settings from the Info structs. */ typedef struct LodePNGDecoderSettings { LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ unsigned ignore_crc; /*ignore CRC checksums*/ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ unsigned remember_unknown_chunks; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } LodePNGDecoderSettings; void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ typedef enum LodePNGFilterStrategy { /*every filter at zero*/ LFS_ZERO, /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ LFS_MINSUM, /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending on the image, this is better or worse than minsum.*/ LFS_ENTROPY, /* Brute-force-search PNG filters by compressing each filter for each scanline. Experimental, very slow, and only rarely gives better compression than MINSUM. */ LFS_BRUTE_FORCE, /*use predefined_filters buffer: you specify the filter type for each scanline*/ LFS_PREDEFINED } LodePNGFilterStrategy; /*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ typedef struct LodePNGColorProfile { unsigned colored; /*not greyscale*/ unsigned key; /*image is not opaque and color key is possible instead of full alpha*/ unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/ unsigned short key_g; unsigned short key_b; unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/ unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ } LodePNGColorProfile; void lodepng_color_profile_init(LodePNGColorProfile* profile); /*Get a LodePNGColorProfile of the image.*/ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, const unsigned char* image, unsigned w, unsigned h, const LodePNGColorMode* mode_in); /*The function LodePNG uses internally to decide the PNG color with auto_convert. Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, const unsigned char* image, unsigned w, unsigned h, const LodePNGColorMode* mode_in); /*Settings for the encoder.*/ typedef struct LodePNGEncoderSettings { LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to completely follow the official PNG heuristic, filter_palette_zero must be true and filter_strategy must be LFS_MINSUM*/ unsigned filter_palette_zero; /*Which filter strategy to use when not using zeroes due to filter_palette_zero. Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ LodePNGFilterStrategy filter_strategy; /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with the same length as the amount of scanlines in the image, and each value must <= 5. You have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ const unsigned char* predefined_filters; /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). If colortype is 3, PLTE is _always_ created.*/ unsigned force_palette; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*add LodePNG identifier and version as a text chunk, for debugging*/ unsigned add_id; /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ unsigned text_compression; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } LodePNGEncoderSettings; void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); #endif /*LODEPNG_COMPILE_ENCODER*/ #if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) /*The settings, state and information for extended encoding and decoding.*/ typedef struct LodePNGState { #ifdef LODEPNG_COMPILE_DECODER LodePNGDecoderSettings decoder; /*the decoding settings*/ #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER LodePNGEncoderSettings encoder; /*the encoding settings*/ #endif /*LODEPNG_COMPILE_ENCODER*/ LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ unsigned error; #ifdef LODEPNG_COMPILE_CPP /* For the lodepng::State subclass. */ virtual ~LodePNGState(){} #endif } LodePNGState; /*init, cleanup and copy functions to use with this struct*/ void lodepng_state_init(LodePNGState* state); void lodepng_state_cleanup(LodePNGState* state); void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); #endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ #ifdef LODEPNG_COMPILE_DECODER /* Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and getting much more information about the PNG image and color mode. */ unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize); /* Read the PNG header, but not the actual data. This returns only the information that is in the header chunk of the PNG, such as width, height and color type. The information is placed in the info_png field of the LodePNGState. */ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ unsigned lodepng_encode(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGState* state); #endif /*LODEPNG_COMPILE_ENCODER*/ /* The lodepng_chunk functions are normally not needed, except to traverse the unknown chunks stored in the LodePNGInfo struct, or add new ones to it. It also allows traversing the chunks of an encoded PNG file yourself. PNG standard chunk naming conventions: First byte: uppercase = critical, lowercase = ancillary Second byte: uppercase = public, lowercase = private Third byte: must be uppercase Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy */ /* Gets the length of the data of the chunk. Total chunk length has 12 bytes more. There must be at least 4 bytes to read from. If the result value is too large, it may be corrupt data. */ unsigned lodepng_chunk_length(const unsigned char* chunk); /*puts the 4-byte type in null terminated string*/ void lodepng_chunk_type(char type[5], const unsigned char* chunk); /*check if the type is the given type*/ unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); /*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); /*0: public, 1: private (see PNG standard)*/ unsigned char lodepng_chunk_private(const unsigned char* chunk); /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); /*get pointer to the data of the chunk, where the input points to the header of the chunk*/ unsigned char* lodepng_chunk_data(unsigned char* chunk); const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); /*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ unsigned lodepng_chunk_check_crc(const unsigned char* chunk); /*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ void lodepng_chunk_generate_crc(unsigned char* chunk); /*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/ unsigned char* lodepng_chunk_next(unsigned char* chunk); const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk); /* Appends chunk to the data in out. The given chunk should already have its chunk header. The out variable and outlength are updated to reflect the new reallocated buffer. Returns error code (0 if it went ok) */ unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk); /* Appends new chunk to out. The chunk to append is given by giving its length, type and data separately. The type is a 4-letter string. The out variable and outlength are updated to reflect the new reallocated buffer. Returne error code (0 if it went ok) */ unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, const char* type, const unsigned char* data); /*Calculate CRC32 of buffer*/ unsigned lodepng_crc32(const unsigned char* buf, size_t len); #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ZLIB /* This zlib part can be used independently to zlib compress and decompress a buffer. It cannot be used to create gzip files however, and it only supports the part of zlib that is required for PNG, it does not support dictionaries. */ #ifdef LODEPNG_COMPILE_DECODER /*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ unsigned lodepng_inflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings); /* Decompresses Zlib data. Reallocates the out buffer and appends the data. The data must be according to the zlib specification. Either, *out must be NULL and *outsize must be 0, or, *out must be a valid buffer and *outsize its size in bytes. out must be freed by user after usage. */ unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* Compresses data with Zlib. Reallocates the out buffer and appends the data. Zlib adds a small header and trailer around the deflate data. The data is output in the format of the zlib specification. Either, *out must be NULL and *outsize must be 0, or, *out must be a valid buffer and *outsize its size in bytes. out must be freed by user after usage. */ unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings); /* Find length-limited Huffman code for given frequencies. This function is in the public interface only for tests, it's used internally by lodepng_deflate. */ unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, size_t numcodes, unsigned maxbitlen); /*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ unsigned lodepng_deflate(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings); #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ #ifdef LODEPNG_COMPILE_DISK /* Load a file from disk into buffer. The function allocates the out buffer, and after usage you should free it. out: output parameter, contains pointer to loaded buffer. outsize: output parameter, size of the allocated out buffer filename: the path to the file to load return value: error code (0 means ok) */ unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); /* Save a file from buffer to disk. Warning, if it exists, this function overwrites the file without warning! buffer: the buffer to write buffersize: size of the buffer to write filename: the path to the file to save to return value: error code (0 means ok) */ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); #endif /*LODEPNG_COMPILE_DISK*/ #ifdef LODEPNG_COMPILE_CPP /* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ namespace lodepng { #ifdef LODEPNG_COMPILE_PNG class State : public LodePNGState { public: State(); State(const State& other); virtual ~State(); State& operator=(const State& other); }; #ifdef LODEPNG_COMPILE_DECODER /* Same as other lodepng::decode, but using a State for more settings and information. */ unsigned decode(std::vector& out, unsigned& w, unsigned& h, State& state, const unsigned char* in, size_t insize); unsigned decode(std::vector& out, unsigned& w, unsigned& h, State& state, const std::vector& in); #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER /* Same as other lodepng::encode, but using a State for more settings and information. */ unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, State& state); unsigned encode(std::vector& out, const std::vector& in, unsigned w, unsigned h, State& state); #endif /*LODEPNG_COMPILE_ENCODER*/ #ifdef LODEPNG_COMPILE_DISK /* Load a file from disk into an std::vector. return value: error code (0 means ok) */ unsigned load_file(std::vector& buffer, const std::string& filename); /* Save the binary data in an std::vector to a file on disk. The file is overwritten without warning. */ unsigned save_file(const std::vector& buffer, const std::string& filename); #endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_PNG */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER /* Zlib-decompress an unsigned char buffer */ unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); /* Zlib-decompress an std::vector */ unsigned decompress(std::vector& out, const std::vector& in, const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); #endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER /* Zlib-compress an unsigned char buffer */ unsigned compress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGCompressSettings& settings = lodepng_default_compress_settings); /* Zlib-compress an std::vector */ unsigned compress(std::vector& out, const std::vector& in, const LodePNGCompressSettings& settings = lodepng_default_compress_settings); #endif /* LODEPNG_COMPILE_ENCODER */ #endif /* LODEPNG_COMPILE_ZLIB */ } /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ /* TODO: [.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often [.] check compatibility with various compilers - done but needs to be redone for every newer version [X] converting color to 16-bit per channel types [ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) [ ] make sure encoder generates no chunks with size > (2^31)-1 [ ] partial decoding (stream processing) [X] let the "isFullyOpaque" function check color keys and transparent palettes too [X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" [ ] don't stop decoding on errors like 69, 57, 58 (make warnings) [ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... [ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes [ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... [ ] allow user to give data (void*) to custom allocator */ #endif /*LODEPNG_H inclusion guard*/ /* LodePNG Documentation --------------------- 0. table of contents -------------------- 1. about 1.1. supported features 1.2. features not supported 2. C and C++ version 3. security 4. decoding 5. encoding 6. color conversions 6.1. PNG color types 6.2. color conversions 6.3. padding bits 6.4. A note about 16-bits per channel and endianness 7. error values 8. chunks and PNG editing 9. compiler support 10. examples 10.1. decoder C++ example 10.2. decoder C example 11. state settings reference 12. changes 13. contact information 1. about -------- PNG is a file format to store raster images losslessly with good compression, supporting different color types and alpha channel. LodePNG is a PNG codec according to the Portable Network Graphics (PNG) Specification (Second Edition) - W3C Recommendation 10 November 2003. The specifications used are: *) Portable Network Graphics (PNG) Specification (Second Edition): http://www.w3.org/TR/2003/REC-PNG-20031110 *) RFC 1950 ZLIB Compressed Data Format version 3.3: http://www.gzip.org/zlib/rfc-zlib.html *) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: http://www.gzip.org/zlib/rfc-deflate.html The most recent version of LodePNG can currently be found at http://lodev.org/lodepng/ LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds extra functionality. LodePNG exists out of two files: -lodepng.h: the header file for both C and C++ -lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage If you want to start using LodePNG right away without reading this doc, get the examples from the LodePNG website to see how to use it in code, or check the smaller examples in chapter 13 here. LodePNG is simple but only supports the basic requirements. To achieve simplicity, the following design choices were made: There are no dependencies on any external library. There are functions to decode and encode a PNG with a single function call, and extended versions of these functions taking a LodePNGState struct allowing to specify or get more information. By default the colors of the raw image are always RGB or RGBA, no matter what color type the PNG file uses. To read and write files, there are simple functions to convert the files to/from buffers in memory. This all makes LodePNG suitable for loading textures in games, demos and small programs, ... It's less suitable for full fledged image editors, loading PNGs over network (it requires all the image data to be available before decoding can begin), life-critical systems, ... 1.1. supported features ----------------------- The following features are supported by the decoder: *) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, or the same color type as the PNG *) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image *) Adam7 interlace and deinterlace for any color type *) loading the image from harddisk or decoding it from a buffer from other sources than harddisk *) support for alpha channels, including RGBA color model, translucent palettes and color keying *) zlib decompression (inflate) *) zlib compression (deflate) *) CRC32 and ADLER32 checksums *) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. *) the following chunks are supported (generated/interpreted) by both encoder and decoder: IHDR: header information PLTE: color palette IDAT: pixel data IEND: the final chunk tRNS: transparency for palettized images tEXt: textual information zTXt: compressed textual information iTXt: international textual information bKGD: suggested background color pHYs: physical dimensions tIME: modification time 1.2. features not supported --------------------------- The following features are _not_ supported: *) some features needed to make a conformant PNG-Editor might be still missing. *) partial loading/stream processing. All data must be available and is processed in one call. *) The following public chunks are not supported but treated as unknown chunks by LodePNG cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT Some of these are not supported on purpose: LodePNG wants to provide the RGB values stored in the pixels, not values modified by system dependent gamma or color models. 2. C and C++ version -------------------- The C version uses buffers allocated with alloc that you need to free() yourself. You need to use init and cleanup functions for each struct whenever using a struct from the C version to avoid exploits and memory leaks. The C++ version has extra functions with std::vectors in the interface and the lodepng::State class which is a LodePNGState with constructor and destructor. These files work without modification for both C and C++ compilers because all the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers ignore it, and the C code is made to compile both with strict ISO C90 and C++. To use the C++ version, you need to rename the source file to lodepng.cpp (instead of lodepng.c), and compile it with a C++ compiler. To use the C version, you need to rename the source file to lodepng.c (instead of lodepng.cpp), and compile it with a C compiler. 3. Security ----------- Even if carefully designed, it's always possible that LodePNG contains possible exploits. If you discover one, please let me know, and it will be fixed. When using LodePNG, care has to be taken with the C version of LodePNG, as well as the C-style structs when working with C++. The following conventions are used for all C-style structs: -if a struct has a corresponding init function, always call the init function when making a new one -if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks -if a struct has a corresponding copy function, use the copy function instead of "=". The destination must also be inited already. 4. Decoding ----------- Decoding converts a PNG compressed image to a raw pixel buffer. Most documentation on using the decoder is at its declarations in the header above. For C, simple decoding can be done with functions such as lodepng_decode32, and more advanced decoding can be done with the struct LodePNGState and lodepng_decode. For C++, all decoding can be done with the various lodepng::decode functions, and lodepng::State can be used for advanced features. When using the LodePNGState, it uses the following fields for decoding: *) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here *) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get *) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use LodePNGInfo info_png -------------------- After decoding, this contains extra information of the PNG image, except the actual pixels, width and height because these are already gotten directly from the decoder functions. It contains for example the original color type of the PNG image, text comments, suggested background color, etc... More details about the LodePNGInfo struct are at its declaration documentation. LodePNGColorMode info_raw ------------------------- When decoding, here you can specify which color type you want the resulting raw image to be. If this is different from the colortype of the PNG, then the decoder will automatically convert the result. This conversion always works, except if you want it to convert a color PNG to greyscale or to a palette with missing colors. By default, 32-bit color is used for the result. LodePNGDecoderSettings decoder ------------------------------ The settings can be used to ignore the errors created by invalid CRC and Adler32 chunks, and to disable the decoding of tEXt chunks. There's also a setting color_convert, true by default. If false, no conversion is done, the resulting data will be as it was in the PNG (after decompression) and you'll have to puzzle the colors of the pixels together yourself using the color type information in the LodePNGInfo. 5. Encoding ----------- Encoding converts a raw pixel buffer to a PNG compressed image. Most documentation on using the encoder is at its declarations in the header above. For C, simple encoding can be done with functions such as lodepng_encode32, and more advanced decoding can be done with the struct LodePNGState and lodepng_encode. For C++, all encoding can be done with the various lodepng::encode functions, and lodepng::State can be used for advanced features. Like the decoder, the encoder can also give errors. However it gives less errors since the encoder input is trusted, the decoder input (a PNG image that could be forged by anyone) is not trusted. When using the LodePNGState, it uses the following fields for encoding: *) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. *) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has *) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use LodePNGInfo info_png -------------------- When encoding, you use this the opposite way as when decoding: for encoding, you fill in the values you want the PNG to have before encoding. By default it's not needed to specify a color type for the PNG since it's automatically chosen, but it's possible to choose it yourself given the right settings. The encoder will not always exactly match the LodePNGInfo struct you give, it tries as close as possible. Some things are ignored by the encoder. The encoder uses, for example, the following settings from it when applicable: colortype and bitdepth, text chunks, time chunk, the color key, the palette, the background color, the interlace method, unknown chunks, ... When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. If the palette contains any colors for which the alpha channel is not 255 (so there are translucent colors in the palette), it'll add a tRNS chunk. LodePNGColorMode info_raw ------------------------- You specify the color type of the raw image that you give to the input here, including a possible transparent color key and palette you happen to be using in your raw image data. By default, 32-bit color is assumed, meaning your input has to be in RGBA format with 4 bytes (unsigned chars) per pixel. LodePNGEncoderSettings encoder ------------------------------ The following settings are supported (some are in sub-structs): *) auto_convert: when this option is enabled, the encoder will automatically choose the smallest possible color mode (including color key) that can encode the colors of all pixels without information loss. *) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, 2 = dynamic huffman tree (best compression). Should be 2 for proper compression. *) use_lz77: whether or not to use LZ77 for compressed block types. Should be true for proper compression. *) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value 2048 by default, but can be set to 32768 for better, but slow, compression. *) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE chunk if force_palette is true. This can used as suggested palette to convert to by viewers that don't support more than 256 colors (if those still exist) *) add_id: add text chunk "Encoder: LodePNG " to the image. *) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. zTXt chunks use zlib compression on the text. This gives a smaller result on large texts but a larger result on small texts (such as a single program name). It's all tEXt or all zTXt though, there's no separate setting per text yet. 6. color conversions -------------------- An important thing to note about LodePNG, is that the color type of the PNG, and the color type of the raw image, are completely independent. By default, when you decode a PNG, you get the result as a raw image in the color type you want, no matter whether the PNG was encoded with a palette, greyscale or RGBA color. And if you encode an image, by default LodePNG will automatically choose the PNG color type that gives good compression based on the values of colors and amount of colors in the image. It can be configured to let you control it instead as well, though. To be able to do this, LodePNG does conversions from one color mode to another. It can convert from almost any color type to any other color type, except the following conversions: RGB to greyscale is not supported, and converting to a palette when the palette doesn't have a required color is not supported. This is not supported on purpose: this is information loss which requires a color reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to grey is easy, but there are multiple ways if you want to give some channels more weight). By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB color, no matter what color type the PNG has. And by default when encoding, LodePNG automatically picks the best color model for the output PNG, and expects the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control the color format of the images yourself, you can skip this chapter. 6.1. PNG color types -------------------- A PNG image can have many color types, ranging from 1-bit color to 64-bit color, as well as palettized color modes. After the zlib decompression and unfiltering in the PNG image is done, the raw pixel data will have that color type and thus a certain amount of bits per pixel. If you want the output raw image after decoding to have another color type, a conversion is done by LodePNG. The PNG specification gives the following color types: 0: greyscale, bit depths 1, 2, 4, 8, 16 2: RGB, bit depths 8 and 16 3: palette, bit depths 1, 2, 4 and 8 4: greyscale with alpha, bit depths 8 and 16 6: RGBA, bit depths 8 and 16 Bit depth is the amount of bits per pixel per color channel. So the total amount of bits per pixel is: amount of channels * bitdepth. 6.2. color conversions ---------------------- As explained in the sections about the encoder and decoder, you can specify color types and bit depths in info_png and info_raw to change the default behaviour. If, when decoding, you want the raw image to be something else than the default, you need to set the color type and bit depth you want in the LodePNGColorMode, or the parameters colortype and bitdepth of the simple decoding function. If, when encoding, you use another color type than the default in the raw input image, you need to specify its color type and bit depth in the LodePNGColorMode of the raw image, or use the parameters colortype and bitdepth of the simple encoding function. If, when encoding, you don't want LodePNG to choose the output PNG color type but control it yourself, you need to set auto_convert in the encoder settings to false, and specify the color type you want in the LodePNGInfo of the encoder (including palette: it can generate a palette if auto_convert is true, otherwise not). If the input and output color type differ (whether user chosen or auto chosen), LodePNG will do a color conversion, which follows the rules below, and may sometimes result in an error. To avoid some confusion: -the decoder converts from PNG to raw image -the encoder converts from raw image to PNG -the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image -the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG -when encoding, the color type in LodePNGInfo is ignored if auto_convert is enabled, it is automatically generated instead -when decoding, the color type in LodePNGInfo is set by the decoder to that of the original PNG image, but it can be ignored since the raw image has the color type you requested instead -if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion between the color types is done if the color types are supported. If it is not supported, an error is returned. If the types are the same, no conversion is done. -even though some conversions aren't supported, LodePNG supports loading PNGs from any colortype and saving PNGs to any colortype, sometimes it just requires preparing the raw image correctly before encoding. -both encoder and decoder use the same color converter. Non supported color conversions: -color to greyscale: no error is thrown, but the result will look ugly because only the red channel is taken -anything to palette when that palette does not have that color in it: in this case an error is thrown Supported color conversions: -anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA -any grey or grey+alpha, to grey or grey+alpha -anything to a palette, as long as the palette has the requested colors in it -removing alpha channel -higher to smaller bitdepth, and vice versa If you want no color conversion to be done (e.g. for speed or control): -In the encoder, you can make it save a PNG with any color type by giving the raw color mode and LodePNGInfo the same color mode, and setting auto_convert to false. -In the decoder, you can make it store the pixel data in the same color type as the PNG has, by setting the color_convert setting to false. Settings in info_raw are then ignored. The function lodepng_convert does the color conversion. It is available in the interface but normally isn't needed since the encoder and decoder already call it. 6.3. padding bits ----------------- In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines have a bit amount that isn't a multiple of 8, then padding bits are used so that each scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. The raw input image you give to the encoder, and the raw output image you get from the decoder will NOT have these padding bits, e.g. in the case of a 1-bit image with a width of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte, not the first bit of a new byte. 6.4. A note about 16-bits per channel and endianness ---------------------------------------------------- LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like for any other color format. The 16-bit values are stored in big endian (most significant byte first) in these arrays. This is the opposite order of the little endian used by x86 CPU's. LodePNG always uses big endian because the PNG file format does so internally. Conversions to other formats than PNG uses internally are not supported by LodePNG on purpose, there are myriads of formats, including endianness of 16-bit colors, the order in which you store R, G, B and A, and so on. Supporting and converting to/from all that is outside the scope of LodePNG. This may mean that, depending on your use case, you may want to convert the big endian output of LodePNG to little endian with a for loop. This is certainly not always needed, many applications and libraries support big endian 16-bit colors anyway, but it means you cannot simply cast the unsigned char* buffer to an unsigned short* buffer on x86 CPUs. 7. error values --------------- All functions in LodePNG that return an error code, return 0 if everything went OK, or a non-zero code if there was an error. The meaning of the LodePNG error values can be retrieved with the function lodepng_error_text: given the numerical error code, it returns a description of the error in English as a string. Check the implementation of lodepng_error_text to see the meaning of each code. 8. chunks and PNG editing ------------------------- If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG editor that should follow the rules about handling of unknown chunks, or if your program is able to read other types of chunks than the ones handled by LodePNG, then that's possible with the chunk functions of LodePNG. A PNG chunk has the following layout: 4 bytes length 4 bytes type name length bytes data 4 bytes CRC 8.1. iterating through chunks ----------------------------- If you have a buffer containing the PNG image data, then the first chunk (the IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the signature of the PNG and are not part of a chunk. But if you start at byte 8 then you have a chunk, and can check the following things of it. NOTE: none of these functions check for memory buffer boundaries. To avoid exploits, always make sure the buffer contains all the data of the chunks. When using lodepng_chunk_next, make sure the returned value is within the allocated memory. unsigned lodepng_chunk_length(const unsigned char* chunk): Get the length of the chunk's data. The total chunk length is this length + 12. void lodepng_chunk_type(char type[5], const unsigned char* chunk): unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): Get the type of the chunk or compare if it's a certain type unsigned char lodepng_chunk_critical(const unsigned char* chunk): unsigned char lodepng_chunk_private(const unsigned char* chunk): unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). Check if the chunk is private (public chunks are part of the standard, private ones not). Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your program doesn't handle that type of unknown chunk. unsigned char* lodepng_chunk_data(unsigned char* chunk): const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): Get a pointer to the start of the data of the chunk. unsigned lodepng_chunk_check_crc(const unsigned char* chunk): void lodepng_chunk_generate_crc(unsigned char* chunk): Check if the crc is correct or generate a correct one. unsigned char* lodepng_chunk_next(unsigned char* chunk): const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these functions do no boundary checking of the allocated data whatsoever, so make sure there is enough data available in the buffer to be able to go to the next chunk. unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk): unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, const char* type, const unsigned char* data): These functions are used to create new chunks that are appended to the data in *out that has length *outlength. The append function appends an existing chunk to the new data. The create function creates a new chunk with the given parameters and appends it. Type is the 4-letter name of the chunk. 8.2. chunks in info_png ----------------------- The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 buffers (each with size) to contain 3 types of unknown chunks: the ones that come before the PLTE chunk, the ones that come between the PLTE and the IDAT chunks, and the ones that come after the IDAT chunks. It's necessary to make the distionction between these 3 cases because the PNG standard forces to keep the ordering of unknown chunks compared to the critical chunks, but does not force any other ordering rules. info_png.unknown_chunks_data[0] is the chunks before PLTE info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT info_png.unknown_chunks_data[2] is the chunks after IDAT The chunks in these 3 buffers can be iterated through and read by using the same way described in the previous subchapter. When using the decoder to decode a PNG, you can make it store all unknown chunks if you set the option settings.remember_unknown_chunks to 1. By default, this option is off (0). The encoder will always encode unknown chunks that are stored in the info_png. If you need it to add a particular chunk that isn't known by LodePNG, you can use lodepng_chunk_append or lodepng_chunk_create to the chunk data in info_png.unknown_chunks_data[x]. Chunks that are known by LodePNG should not be added in that way. E.g. to make LodePNG add a bKGD chunk, set background_defined to true and add the correct parameters there instead. 9. compiler support ------------------- No libraries other than the current standard C library are needed to compile LodePNG. For the C++ version, only the standard C++ library is needed on top. Add the files lodepng.c(pp) and lodepng.h to your project, include lodepng.h where needed, and your program can read/write PNG files. It is compatible with C90 and up, and C++03 and up. If performance is important, use optimization when compiling! For both the encoder and decoder, this makes a large difference. Make sure that LodePNG is compiled with the same compiler of the same version and with the same settings as the rest of the program, or the interfaces with std::vectors and std::strings in C++ can be incompatible. CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. *) gcc and g++ LodePNG is developed in gcc so this compiler is natively supported. It gives no warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ version 4.7.1 on Linux, 32-bit and 64-bit. *) Clang Fully supported and warning-free. *) Mingw The Mingw compiler (a port of gcc for Windows) should be fully supported by LodePNG. *) Visual Studio and Visual C++ Express Edition LodePNG should be warning-free with warning level W4. Two warnings were disabled with pragmas though: warning 4244 about implicit conversions, and warning 4996 where it wants to use a non-standard function fopen_s instead of the standard C fopen. Visual Studio may want "stdafx.h" files to be included in each source file and give an error "unexpected end of file while looking for precompiled header". This is not standard C++ and will not be added to the stock LodePNG. You can disable it for lodepng.cpp only by right clicking it, Properties, C/C++, Precompiled Headers, and set it to Not Using Precompiled Headers there. NOTE: Modern versions of VS should be fully supported, but old versions, e.g. VS6, are not guaranteed to work. *) Compilers on Macintosh LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for C and C++. *) Other Compilers If you encounter problems on any compilers, feel free to let me know and I may try to fix it if the compiler is modern and standards complient. 10. examples ------------ This decoder example shows the most basic usage of LodePNG. More complex examples can be found on the LodePNG website. 10.1. decoder C++ example ------------------------- #include "lodepng.h" #include int main(int argc, char *argv[]) { const char* filename = argc > 1 ? argv[1] : "test.png"; //load and decode std::vector image; unsigned width, height; unsigned error = lodepng::decode(image, width, height, filename); //if there's an error, display it if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... } 10.2. decoder C example ----------------------- #include "lodepng.h" int main(int argc, char *argv[]) { unsigned error; unsigned char* image; size_t width, height; const char* filename = argc > 1 ? argv[1] : "test.png"; error = lodepng_decode32_file(&image, &width, &height, filename); if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); / * use image here * / free(image); return 0; } 11. state settings reference ---------------------------- A quick reference of some settings to set on the LodePNGState For decoding: state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums state.decoder.zlibsettings.custom_...: use custom inflate function state.decoder.ignore_crc: ignore CRC checksums state.decoder.color_convert: convert internal PNG color to chosen one state.decoder.read_text_chunks: whether to read in text metadata chunks state.decoder.remember_unknown_chunks: whether to read in unknown chunks state.info_raw.colortype: desired color type for decoded image state.info_raw.bitdepth: desired bit depth for decoded image state.info_raw....: more color settings, see struct LodePNGColorMode state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo For encoding: state.encoder.zlibsettings.btype: disable compression by setting it to 0 state.encoder.zlibsettings.use_lz77: use LZ77 in compression state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching state.encoder.zlibsettings.lazymatching: try one more LZ77 matching state.encoder.zlibsettings.custom_...: use custom deflate function state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png state.encoder.filter_palette_zero: PNG filter strategy for palette state.encoder.filter_strategy: PNG filter strategy to encode with state.encoder.force_palette: add palette even if not encoding to one state.encoder.add_id: add LodePNG identifier and version as a text chunk state.encoder.text_compression: use compressed text chunks for metadata state.info_raw.colortype: color type of raw input image you provide state.info_raw.bitdepth: bit depth of raw input image you provide state.info_raw: more color settings, see struct LodePNGColorMode state.info_png.color.colortype: desired color type if auto_convert is false state.info_png.color.bitdepth: desired bit depth if auto_convert is false state.info_png.color....: more color settings, see struct LodePNGColorMode state.info_png....: more PNG related settings, see struct LodePNGInfo 12. changes ----------- The version number of LodePNG is the date of the change given in the format yyyymmdd. Some changes aren't backwards compatible. Those are indicated with a (!) symbol. *) 27 nov 2016: grey+alpha auto color model detection bugfix *) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). *) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within the limits of pure C90). *) 08 dec 2015: Made load_file function return error if file can't be opened. *) 24 okt 2015: Bugfix with decoding to palette output. *) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. *) 23 aug 2014: Reduced needless memory usage of decoder. *) 28 jun 2014: Removed fix_png setting, always support palette OOB for simplicity. Made ColorProfile public. *) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. *) 22 dec 2013: Power of two windowsize required for optimization. *) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. *) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). *) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_" prefix for the custom allocators and made it possible with a new #define to use custom ones in your project without needing to change lodepng's code. *) 28 jan 2013: Bugfix with color key. *) 27 okt 2012: Tweaks in text chunk keyword length error handling. *) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode. (no palette). Better deflate tree encoding. New compression tweak settings. Faster color conversions while decoding. Some internal cleanups. *) 23 sep 2012: Reduced warnings in Visual Studio a little bit. *) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions and made it work with function pointers instead. *) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc and free functions and toggle #defines from compiler flags. Small fixes. *) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible. *) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed redundant C++ codec classes. Reduced amount of structs. Everything changed, but it is cleaner now imho and functionality remains the same. Also fixed several bugs and shrunk the implementation code. Made new samples. *) 6 nov 2011 (!): By default, the encoder now automatically chooses the best PNG color model and bit depth, based on the amount and type of colors of the raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. *) 9 okt 2011: simpler hash chain implementation for the encoder. *) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. *) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. A bug with the PNG filtertype heuristic was fixed, so that it chooses much better ones (it's quite significant). A setting to do an experimental, slow, brute force search for PNG filter types is added. *) 17 aug 2011 (!): changed some C zlib related function names. *) 16 aug 2011: made the code less wide (max 120 characters per line). *) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. *) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. *) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman to optimize long sequences of zeros. *) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and LodePNG_InfoColor_canHaveAlpha functions for convenience. *) 7 nov 2010: added LodePNG_error_text function to get error code description. *) 30 okt 2010: made decoding slightly faster *) 26 okt 2010: (!) changed some C function and struct names (more consistent). Reorganized the documentation and the declaration order in the header. *) 08 aug 2010: only changed some comments and external samples. *) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. *) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. *) 02 sep 2008: fixed bug where it could create empty tree that linux apps could read by ignoring the problem but windows apps couldn't. *) 06 jun 2008: added more error checks for out of memory cases. *) 26 apr 2008: added a few more checks here and there to ensure more safety. *) 06 mar 2008: crash with encoding of strings fixed *) 02 feb 2008: support for international text chunks added (iTXt) *) 23 jan 2008: small cleanups, and #defines to divide code in sections *) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. *) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. *) 17 jan 2008: ability to encode and decode compressed zTXt chunks added Also various fixes, such as in the deflate and the padding bits code. *) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved filtering code of encoder. *) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A C++ wrapper around this provides an interface almost identical to before. Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code are together in these files but it works both for C and C++ compilers. *) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks *) 30 aug 2007: bug fixed which makes this Borland C++ compatible *) 09 aug 2007: some VS2005 warnings removed again *) 21 jul 2007: deflate code placed in new namespace separate from zlib code *) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images *) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing invalid std::vector element [0] fixed, and level 3 and 4 warnings removed *) 02 jun 2007: made the encoder add a tag with version by default *) 27 may 2007: zlib and png code separated (but still in the same file), simple encoder/decoder functions added for more simple usage cases *) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), moved some examples from here to lodepng_examples.cpp *) 12 may 2007: palette decoding bug fixed *) 24 apr 2007: changed the license from BSD to the zlib license *) 11 mar 2007: very simple addition: ability to encode bKGD chunks. *) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding palettized PNG images. Plus little interface change with palette and texts. *) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. Fixed a bug where the end code of a block had length 0 in the Huffman tree. *) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented and supported by the encoder, resulting in smaller PNGs at the output. *) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. *) 24 jan 2007: gave encoder an error interface. Added color conversion from any greyscale type to 8-bit greyscale with or without alpha. *) 21 jan 2007: (!) Totally changed the interface. It allows more color types to convert to and is more uniform. See the manual for how it works now. *) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: encode/decode custom tEXt chunks, separate classes for zlib & deflate, and at last made the decoder give errors for incorrect Adler32 or Crc. *) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. *) 29 dec 2006: Added support for encoding images without alpha channel, and cleaned out code as well as making certain parts faster. *) 28 dec 2006: Added "Settings" to the encoder. *) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. Removed some code duplication in the decoder. Fixed little bug in an example. *) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. Fixed a bug of the decoder with 16-bit per color. *) 15 okt 2006: Changed documentation structure *) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the given image buffer, however for now it's not compressed. *) 08 sep 2006: (!) Changed to interface with a Decoder class *) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different way. Renamed decodePNG to decodePNGGeneric. *) 29 jul 2006: (!) Changed the interface: image info is now returned as a struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. *) 28 jul 2006: Cleaned the code and added new error checks. Corrected terminology "deflate" into "inflate". *) 23 jun 2006: Added SDL example in the documentation in the header, this example allows easy debugging by displaying the PNG and its transparency. *) 22 jun 2006: (!) Changed way to obtain error value. Added loadFile function for convenience. Made decodePNG32 faster. *) 21 jun 2006: (!) Changed type of info vector to unsigned. Changed position of palette in info vector. Fixed an important bug that happened on PNGs with an uncompressed block. *) 16 jun 2006: Internally changed unsigned into unsigned where needed, and performed some optimizations. *) 07 jun 2006: (!) Renamed functions to decodePNG and placed them in LodePNG namespace. Changed the order of the parameters. Rewrote the documentation in the header. Renamed files to lodepng.cpp and lodepng.h *) 22 apr 2006: Optimized and improved some code *) 07 sep 2005: (!) Changed to std::vector interface *) 12 aug 2005: Initial release (C++, decoder only) 13. contact information ----------------------- Feel free to contact me with suggestions, problems, comments, ... concerning LodePNG. If you encounter a PNG image that doesn't work properly with this decoder, feel free to send it and I'll use it to find and fix the problem. My email address is (puzzle the account and domain together with an @ symbol): Domain: gmail dot com. Account: lode dot vandevenne. Copyright (c) 2005-2016 Lode Vandevenne */ CubicSDR-0.2.3/external/rs232/000077500000000000000000000000001322677621400156215ustar00rootroot00000000000000CubicSDR-0.2.3/external/rs232/README000066400000000000000000000024011322677621400164760ustar00rootroot00000000000000Cross-platform serial / RS232 library Version 0.21, 11/10/2015 The MIT License (MIT) Supported platforms: - Windows (XP / Win7, possibly 8 and 10) - Linux - MacOS X Copyright (c) 2007 - 2015 Frédéric Meslin Contact: fredericmeslin@hotmail.com, @marzacdev 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. CubicSDR-0.2.3/external/rs232/README.md000066400000000000000000000004511322677621400171000ustar00rootroot00000000000000# rs232 C / C++ RS232 cross-platform serial library Version 0.21, 11/10/2015 Supported platforms: - Windows (XP / Win7, possibly 8 and 10) - Linux - MacOS X Copyright (c) 2013-2015 Frédéric Meslin Email: fredericmeslin@hotmail.com Website: www.fredslab.net Twitter: @marzacdev CubicSDR-0.2.3/external/rs232/rs232-linux.cpp000066400000000000000000000165671322677621400203540ustar00rootroot00000000000000/* Cross-platform serial / RS232 library Version 0.21, 11/10/2015 -> LINUX and MacOS implementation -> rs232-linux.c The MIT License (MIT) Copyright (c) 2013-2015 Frédéric Meslin, Florent Touchard Email: fredericmeslin@hotmail.com Website: www.fredslab.net Twitter: @marzacdev 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. */ #if defined(__unix__) || defined(__unix) || \ defined(__APPLE__) && defined(__MACH__) #define _DARWIN_C_SOURCE #include "rs232.h" #include #define __USE_MISC // For CRTSCTS #include #include #include #define __USE_SVID // For strdup #include #include #include /*****************************************************************************/ /** Base name for COM devices */ #if defined(__APPLE__) && defined(__MACH__) static const char * devBases[] = { "tty." }; static int noBases = 1; #else static const char * devBases[] = { "ttyACM", "ttyUSB", "rfcomm", "ttyS" }; static int noBases = 4; #endif /*****************************************************************************/ typedef struct { char * port; int handle; } COMDevice; #define COM_MAXDEVICES 64 static COMDevice comDevices[COM_MAXDEVICES]; static int noDevices = 0; /*****************************************************************************/ /** Private functions */ void _AppendDevices(const char * base); int _BaudFlag(int BaudRate); /*****************************************************************************/ int comEnumerate() { for (int i = 0; i < noDevices; i++) { if (comDevices[i].port) free(comDevices[i].port); comDevices[i].port = NULL; } noDevices = 0; for (int i = 0; i < noBases; i++) _AppendDevices(devBases[i]); return noDevices; } void comTerminate() { comCloseAll(); for (int i = 0; i < noDevices; i++) { if (comDevices[i].port) free(comDevices[i].port); comDevices[i].port = NULL; } } int comGetNoPorts() { return noDevices; } /*****************************************************************************/ int comFindPort(const char * name) { int p; for (p = 0; p < noDevices; p++) if (strcmp(name, comDevices[p].port) == 0) return p; return -1; } const char * comGetInternalName(int index) { #define COM_MAXNAME 128 static char name[COM_MAXNAME]; if (index >= noDevices || index < 0) return NULL; sprintf(name, "/dev/%s", comDevices[index].port); return name; } const char * comGetPortName(int index) { if (index >= noDevices || index < 0) return NULL; return comDevices[index].port; } /*****************************************************************************/ int comOpen(int index, int baudrate) { if (index >= noDevices || index < 0) return 0; // Close if already open COMDevice * com = &comDevices[index]; if (com->handle >= 0) comClose(index); // Open port printf("Try %s \n", comGetInternalName(index)); int handle = open(comGetInternalName(index), O_RDWR | O_NOCTTY | O_NDELAY); if (handle < 0) return 0; printf("Open %s \n", comGetInternalName(index)); // General configuration struct termios config; memset(&config, 0, sizeof(config)); tcgetattr(handle, &config); config.c_iflag &= ~(INLCR | ICRNL); config.c_iflag |= IGNPAR | IGNBRK; config.c_oflag &= ~(OPOST | ONLCR | OCRNL); config.c_cflag &= ~(PARENB | PARODD | CSTOPB | CSIZE | CRTSCTS); config.c_cflag |= CLOCAL | CREAD | CS8; config.c_lflag &= ~(ICANON | ISIG | ECHO); int flag = _BaudFlag(baudrate); cfsetospeed(&config, flag); cfsetispeed(&config, flag); // Timeouts configuration config.c_cc[VTIME] = 1; config.c_cc[VMIN] = 0; //fcntl(handle, F_SETFL, FNDELAY); // Validate configuration if (tcsetattr(handle, TCSANOW, &config) < 0) { close(handle); return 0; } com->handle = handle; return 1; } void comClose(int index) { if (index >= noDevices || index < 0) return; COMDevice * com = &comDevices[index]; if (com->handle < 0) return; tcdrain(com->handle); close(com->handle); com->handle = -1; } void comCloseAll() { for (int i = 0; i < noDevices; i++) comClose(i); } /*****************************************************************************/ int comWrite(int index, const char * buffer, size_t len) { if (index >= noDevices || index < 0) return 0; if (comDevices[index].handle <= 0) return 0; int res = write(comDevices[index].handle, buffer, len); if (res < 0) res = 0; return res; } int comRead(int index, char * buffer, size_t len) { if (index >= noDevices || index < 0) return 0; if (comDevices[index].handle <= 0) return 0; int res = read(comDevices[index].handle, buffer, len); if (res < 0) res = 0; return res; } /*****************************************************************************/ int _BaudFlag(int BaudRate) { switch(BaudRate) { case 50: return B50; break; case 110: return B110; break; case 134: return B134; break; case 150: return B150; break; case 200: return B200; break; case 300: return B300; break; case 600: return B600; break; case 1200: return B1200; break; case 1800: return B1800; break; case 2400: return B2400; break; case 4800: return B4800; break; case 9600: return B9600; break; case 19200: return B19200; break; case 38400: return B38400; break; case 57600: return B57600; break; case 115200: return B115200; break; case 230400: return B230400; break; default : return B0; break; } } void _AppendDevices(const char * base) { int baseLen = strlen(base); struct dirent * dp; // Enumerate devices DIR * dirp = opendir("/dev"); while ((dp = readdir(dirp)) && noDevices < COM_MAXDEVICES) { if (strlen(dp->d_name) >= baseLen) { if (memcmp(base, dp->d_name, baseLen) == 0) { COMDevice * com = &comDevices[noDevices ++]; com->port = (char *) strdup(dp->d_name); com->handle = -1; } } } closedir(dirp); } #endif // unix CubicSDR-0.2.3/external/rs232/rs232-win.cpp000066400000000000000000000160601322677621400177760ustar00rootroot00000000000000/* Cross-platform serial / RS232 library Version 0.21, 11/10/2015 -> WIN32 implementation -> rs232-win.c The MIT License (MIT) Copyright (c) 2013-2015 Frédéric Meslin, Florent Touchard Email: fredericmeslin@hotmail.com Website: www.fredslab.net Twitter: @marzacdev 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. */ #ifdef _WIN32 #include "rs232.h" #include #include #include /*****************************************************************************/ typedef struct { int port; void * handle; } COMDevice; /*****************************************************************************/ #define COM_MAXDEVICES 64 static COMDevice comDevices[COM_MAXDEVICES]; static int noDevices = 0; #define COM_MINDEVNAME 16384 const char * comPtn = "COM???"; /*****************************************************************************/ const char * findPattern(const char * string, const char * pattern, int * value); const char * portInternalName(int index); /*****************************************************************************/ int comEnumerate() { // Get devices information text size_t size = COM_MINDEVNAME; char * list = (char *) malloc(size); SetLastError(0); QueryDosDeviceA(NULL, list, size); while (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { size *= 2; char * nlist = (char *) realloc(list, size); if (!nlist) { free(list); return 0; } list = nlist; SetLastError(0); QueryDosDeviceA(NULL, list, size); } // Gather all COM ports int port; const char * nlist = findPattern(list, comPtn, &port); noDevices = 0; while(port > 0 && noDevices < COM_MAXDEVICES) { COMDevice * com = &comDevices[noDevices ++]; com->port = port; com->handle = 0; nlist = findPattern(nlist, comPtn, &port); } free(list); return noDevices; } void comTerminate() { comCloseAll(); } int comGetNoPorts() { return noDevices; } /*****************************************************************************/ const char * comGetPortName(int index) { #define COM_MAXNAME 32 static char name[COM_MAXNAME]; if (index < 0 || index >= noDevices) return 0; sprintf(name, "COM%i", comDevices[index].port); return name; } int comFindPort(const char * name) { for (int i = 0; i < noDevices; i++) if (strcmp(name, comGetPortName(i)) == 0) return i; return -1; } const char * comGetInternalName(int index) { #define COM_MAXNAME 32 static char name[COM_MAXNAME]; if (index < 0 || index >= noDevices) return 0; sprintf(name, "//./COM%i", comDevices[index].port); return name; } /*****************************************************************************/ int comOpen(int index, int baudrate) { DCB config; COMMTIMEOUTS timeouts; if (index < 0 || index >= noDevices) return 0; // Close if already open COMDevice * com = &comDevices[index]; if (com->handle) comClose(index); // Open COM port void * handle = CreateFileA(comGetInternalName(index), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (handle == INVALID_HANDLE_VALUE) return 0; com->handle = handle; // Prepare read / write timeouts SetupComm(handle, 64, 64); timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutConstant = 0; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutMultiplier = 0; SetCommTimeouts(handle, &timeouts); // Prepare serial communication format GetCommState(handle, &config); config.BaudRate = baudrate; config.fBinary = true; config.fParity = 0; config.fErrorChar = 0; config.fNull = 0; config.fAbortOnError = 0; config.ByteSize = 8; config.Parity = 0; config.StopBits = 0; config.EvtChar = '\n'; // Set the port state if (SetCommState(handle, &config) == 0) { CloseHandle(handle); return 0; } return 1; } void comClose(int index) { if (index < 0 || index >= noDevices) return; COMDevice * com = &comDevices[index]; if (!com->handle) return; CloseHandle(com->handle); com->handle = 0; } void comCloseAll() { for (int i = 0; i < noDevices; i++) comClose(i); } /*****************************************************************************/ int comWrite(int index, const char * buffer, size_t len) { if (index < 0 || index >= noDevices) return 0; COMDevice * com = &comDevices[index]; uint32_t bytes = 0; WriteFile(com->handle, buffer, len, (LPDWORD)&bytes, NULL); return bytes; } int comRead(int index, char * buffer, size_t len) { if (index < 0 || index >= noDevices) return 0; COMDevice * com = &comDevices[index]; uint32_t bytes = 0; ReadFile(com->handle, buffer, len, (LPDWORD)&bytes, NULL); return bytes; } /*****************************************************************************/ const char * findPattern(const char * string, const char * pattern, int * value) { char c, n = 0; const char * sp = string; const char * pp = pattern; // Check for the string pattern while (1) { c = *sp ++; if (c == '\0') { if (*pp == '?') break; if (*sp == '\0') break; n = 0; pp = pattern; }else{ if (*pp == '?') { // Expect a digit if (c >= '0' && c <= '9') { n = n * 10 + (c - '0'); if (*pp ++ == '\0') break; }else{ n = 0; pp = comPtn; } }else{ // Expect a character if (c == *pp) { if (*pp ++ == '\0') break; }else{ n = 0; pp = comPtn; } } } } // Return the value * value = n; return sp; } #endif // _WIN32 CubicSDR-0.2.3/external/rs232/rs232.h000066400000000000000000000120401322677621400166420ustar00rootroot00000000000000/* Cross-platform serial / RS232 library Version 0.21, 11/10/2015 -> All platforms header -> rs232.h The MIT License (MIT) Copyright (c) 2013-2015 Frédéric Meslin, Florent Touchard Email: fredericmeslin@hotmail.com Website: www.fredslab.net Twitter: @marzacdev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef RS232_H #define RS232_H // TODO: convert to C++ class //#ifdef __cplusplus //extern "C" { //#endif #include #include /*****************************************************************************/ /* Doxywizard specific */ /** * \mainpage RS232 * \section intro_sec C / C++ RS232 cross-platform serial library * Version 0.21 - 11/10/2015 * * Supported platforms: * - Windows (XP / Win7, possibly 8 and 10) * - Linux * - MacOS X * * Copyright (c) 2013-2015 Frédéric Meslin, Florent Touchard
* Email: fredericmeslin@hotmail.com
* Website: www.fredslab.net
* Twitter: \@marzacdev
*/ /*****************************************************************************/ /** * \fn int comEnumerate() * \brief Enumerate available serial ports (Serial, USB serial, Bluetooth serial) * \return number of enumerated ports */ int comEnumerate(); /** * \fn int comGetNoPorts() * \brief Return the number of enumerated ports * \return number of enumerated ports */ int comGetNoPorts(); /** * \fn int comTerminate() * \brief Release ports and memory resources used by the library */ void comTerminate(); /** * \fn const char * comGetPortName(int index) * \brief Get port user-friendly name * \param[in] index port index * \return null terminated port name */ const char * comGetPortName(int index); /** * \fn const char * comGetInternalName(int index) * \brief Get port operating-system name * \param[in] index port index * \return null terminated port name */ const char * comGetInternalName(int index); /** * \fn int comFindPort(const char * name) * \brief Try to find a port given its user-friendly name * \param[in] name port name (case sensitive) * \return index of found port or -1 if not enumerated */ int comFindPort(const char * name); /*****************************************************************************/ /** * \fn int comOpen(int index, int baudrate) * \brief Try to open a port at a specific baudrate * \brief (No parity, single stop bit, no hardware flow control) * \param[in] index port index * \param[in] baudrate port baudrate * \return 1 if opened, 0 if not available */ int comOpen(int index, int baudrate); /** * \fn void comClose(int index) * \brief Close an opened port * \param[in] index port index */ void comClose(int index); /** * \fn void comCloseAll() * \brief Close all opened ports */ void comCloseAll(); /*****************************************************************************/ /** * \fn int comWrite(int index, const char * buffer, size_t len) * \brief Write data to the port (non-blocking) * \param[in] index port index * \param[in] buffer pointer to transmit buffer * \param[in] len length of transmit buffer in bytes * \return number of bytes transferred */ int comWrite(int index, const char * buffer, size_t len); /** * \fn int comRead(int index, const char * buffer, size_t len) * \brief Read data from the port (non-blocking) * \param[in] index port index * \param[in] buffer pointer to receive buffer * \param[in] len length of receive buffer in bytes * \return number of bytes transferred */ int comRead(int index, char * buffer, size_t len); //#ifdef __cplusplus //} //#endif #endif // RS232_H CubicSDR-0.2.3/font/000077500000000000000000000000001322677621400140725ustar00rootroot00000000000000CubicSDR-0.2.3/font/vera_sans_mono12.bmfc000066400000000000000000000020101322677621400200700ustar00rootroot00000000000000# AngelCode Bitmap Font Generator configuration file fileVersion=1 # font settings fontName=Bitstream Vera Sans Mono fontFile=VeraMono.ttf charSet=0 fontSize=12 aa=1 scaleH=100 useSmoothing=0 isBold=0 isItalic=0 useUnicode=1 disableBoxChars=1 outputInvalidCharGlyph=0 dontIncludeKerningPairs=0 useHinting=1 renderFromOutline=0 useClearType=0 # character alignment paddingDown=0 paddingUp=0 paddingRight=0 paddingLeft=0 spacingHoriz=1 spacingVert=1 useFixedHeight=1 forceZero=0 # output file outWidth=256 outHeight=128 outBitDepth=32 fontDescFormat=0 fourChnlPacked=0 textureFormat=png textureCompression=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 invA=0 invR=0 invG=0 invB=0 # outline outlineThickness=0 # selected chars chars=32-126,160-255,262-263,268-269,273,286-287,304-305,321-322,338-339,350-353,376,381-382,402,710-711 chars=728-733,937,960,8211-8212,8216-8218,8220-8222,8224-8226,8230,8240,8249-8250,8364,8482,8706,8710 chars=8719,8721-8722,8725,8729-8730,8734,8747,8776,8800,8804-8805,9674 # imported icon images CubicSDR-0.2.3/font/vera_sans_mono12.fnt000066400000000000000000000705321322677621400177660ustar00rootroot00000000000000info face="Bitstream Vera Sans Mono" size=12 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 common lineHeight=12 base=10 scaleW=256 scaleH=128 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 page id=0 file="vera_sans_mono12_0.png" chars count=254 char id=32 x=252 y=0 width=3 height=12 xoffset=-1 yoffset=0 xadvance=6 page=0 chnl=15 char id=33 x=100 y=65 width=1 height=12 xoffset=3 yoffset=0 xadvance=6 page=0 chnl=15 char id=34 x=24 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=35 x=21 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=36 x=186 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=37 x=35 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=38 x=192 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=39 x=102 y=65 width=1 height=12 xoffset=3 yoffset=0 xadvance=6 page=0 chnl=15 char id=40 x=80 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=41 x=74 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=42 x=216 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=43 x=222 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=44 x=104 y=65 width=1 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=45 x=36 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=46 x=106 y=65 width=1 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=47 x=246 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=48 x=0 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=49 x=6 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=50 x=12 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=51 x=18 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=52 x=24 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=53 x=30 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=54 x=36 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=55 x=42 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=56 x=48 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=57 x=54 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=58 x=90 y=65 width=1 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=59 x=86 y=65 width=1 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=60 x=72 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=61 x=78 y=13 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=62 x=84 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=63 x=218 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=64 x=96 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=65 x=102 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=66 x=108 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=67 x=114 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=68 x=120 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=69 x=126 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=70 x=132 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=71 x=138 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=72 x=144 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=73 x=150 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=74 x=188 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=75 x=162 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=76 x=168 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=77 x=174 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=78 x=180 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=79 x=186 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=80 x=192 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=81 x=198 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=82 x=42 y=0 width=6 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=83 x=204 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=84 x=210 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=85 x=216 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=86 x=222 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=87 x=77 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=88 x=228 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=89 x=234 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=90 x=240 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=91 x=71 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=92 x=0 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=93 x=56 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=94 x=168 y=0 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=95 x=18 y=26 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=96 x=83 y=65 width=2 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=97 x=30 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=98 x=36 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=99 x=213 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=100 x=48 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=101 x=54 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=102 x=138 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=103 x=66 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=104 x=72 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=105 x=78 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=106 x=252 y=13 width=3 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=107 x=90 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=108 x=96 y=26 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=109 x=102 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=110 x=108 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=111 x=114 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=112 x=120 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=113 x=126 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=114 x=163 y=52 width=4 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=115 x=138 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=116 x=168 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=117 x=150 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=118 x=156 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=119 x=162 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=120 x=168 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=121 x=174 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=122 x=180 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=123 x=178 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=124 x=92 y=65 width=1 height=12 xoffset=3 yoffset=0 xadvance=6 page=0 chnl=15 char id=125 x=203 y=52 width=4 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=126 x=204 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=160 x=252 y=26 width=3 height=12 xoffset=-1 yoffset=0 xadvance=6 page=0 chnl=15 char id=161 x=94 y=65 width=1 height=12 xoffset=3 yoffset=0 xadvance=6 page=0 chnl=15 char id=162 x=143 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=163 x=228 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=164 x=234 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=165 x=240 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=166 x=98 y=65 width=1 height=12 xoffset=3 yoffset=0 xadvance=6 page=0 chnl=15 char id=167 x=148 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=168 x=28 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=169 x=140 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=170 x=233 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=171 x=158 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=172 x=12 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=173 x=20 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=174 x=7 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=175 x=32 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=176 x=16 y=65 width=3 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=177 x=18 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=178 x=252 y=52 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=179 x=248 y=52 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=180 x=77 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=181 x=84 y=0 width=6 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=182 x=24 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=183 x=88 y=65 width=1 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=184 x=59 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=185 x=8 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=186 x=243 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=187 x=228 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=188 x=133 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=189 x=161 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=190 x=49 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=191 x=238 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=192 x=216 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=193 x=36 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=194 x=42 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=195 x=48 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=196 x=54 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=197 x=60 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=198 x=91 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=199 x=66 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=200 x=72 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=201 x=78 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=202 x=84 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=203 x=90 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=204 x=96 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=205 x=102 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=206 x=108 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=207 x=114 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=208 x=126 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=209 x=120 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=210 x=126 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=211 x=132 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=212 x=138 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=213 x=144 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=214 x=150 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=215 x=156 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=216 x=119 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=217 x=162 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=218 x=168 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=219 x=174 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=220 x=180 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=221 x=186 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=222 x=192 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=223 x=90 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=224 x=198 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=225 x=204 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=226 x=210 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=227 x=216 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=228 x=222 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=229 x=228 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=230 x=234 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=231 x=193 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=232 x=240 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=233 x=246 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=234 x=0 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=235 x=6 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=236 x=12 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=237 x=18 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=238 x=24 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=239 x=30 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=240 x=36 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=241 x=42 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=242 x=48 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=243 x=54 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=244 x=60 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=245 x=66 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=246 x=72 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=247 x=78 y=52 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=248 x=84 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=249 x=90 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=250 x=96 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=251 x=102 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=252 x=108 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=253 x=114 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=254 x=120 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=255 x=126 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=262 x=30 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=263 x=198 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=268 x=132 y=52 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=269 x=223 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=273 x=105 y=0 width=6 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=286 x=174 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=287 x=180 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=304 x=198 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=305 x=204 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=321 x=98 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=322 x=208 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=338 x=210 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=339 x=228 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=350 x=234 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=351 x=240 y=0 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=352 x=60 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=353 x=66 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=376 x=156 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=381 x=246 y=13 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=382 x=6 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=402 x=70 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=710 x=48 y=65 width=3 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=711 x=52 y=65 width=3 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=728 x=40 y=65 width=3 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=729 x=96 y=65 width=1 height=12 xoffset=3 yoffset=0 xadvance=6 page=0 chnl=15 char id=730 x=44 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=731 x=62 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=732 x=183 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=733 x=173 y=52 width=4 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=937 x=12 y=26 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=960 x=63 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8211 x=112 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8212 x=56 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8216 x=108 y=65 width=1 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8217 x=110 y=65 width=1 height=12 xoffset=3 yoffset=0 xadvance=6 page=0 chnl=15 char id=8218 x=112 y=65 width=1 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8220 x=0 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8221 x=252 y=39 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8222 x=4 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8224 x=24 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=8225 x=42 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=8226 x=12 y=65 width=3 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8230 x=60 y=26 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8240 x=28 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8249 x=65 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8250 x=68 y=65 width=2 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8364 x=84 y=26 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8482 x=14 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8706 x=153 y=52 width=4 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=8710 x=0 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8719 x=132 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=8721 x=144 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=8722 x=186 y=26 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8725 x=192 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=8729 x=114 y=65 width=1 height=12 xoffset=2 yoffset=0 xadvance=6 page=0 chnl=15 char id=8730 x=154 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8734 x=147 y=0 width=6 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8747 x=198 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=8776 x=210 y=26 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8800 x=222 y=26 width=5 height=12 xoffset=0 yoffset=0 xadvance=6 page=0 chnl=15 char id=8804 x=246 y=26 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=8805 x=0 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 char id=9674 x=6 y=39 width=5 height=12 xoffset=1 yoffset=0 xadvance=6 page=0 chnl=15 CubicSDR-0.2.3/font/vera_sans_mono12_0.png000066400000000000000000000055611322677621400202020ustar00rootroot00000000000000‰PNG  IHDR€äµ· 8IDATxœíÙ–«8 E©Zùÿ_¦ŸÜ­Véhðij_„­É² „º÷8y çyžò“ôñ{µä{¼;&?˜‡p Vê_¡»Ê®Â_mGêŸeW5~Ú—–°ŸŸŸÙ@'²·äçyž²;–rÙϳ•õC·±ä£ ¸zm{9tx9ªö‰rŠüÉŒEƧJQ{íC”Ÿ=ž>¤§µ×s`¤F{Æ^öuû øTXeÎ{²Ê9tÉGˆâ®ÚõrÝãO¥­5ȧ(Ç(Ž¿²ö¿?P-{}«cÍ¡QýÕ~Q[÷Àyú«¥µë]"Ç­~ÕbÔ1RÐRgt\ÝI´¯³|“Dã'år'kß-õXf&š—/iKû¬¯l,_¤žl]#_Gë$£Û³Å[6ê}GƒšÑi h¤Ãj“ ²' (öH'òÇjg£¾Èï8Ÿõ™ñµñd²OÔ7²ÅåÙ‹âõòcÙñäÈN&ÿ™ö(†(WÑùwR¢WߌQùYy¶Ðlµ6rånm¤N©«÷ŠáÝ·y÷‹:.$«èB¾µ6Z¯——OÝ¿â+"cÏ#ã‹®mkf<Ö• úDcÐÎ!½­Ÿåw%‡î`IFyôÐC:]ÖYƒâõ9Ž¿—{‘¿³Š¢éB‹FofÇ#ãj¯ó©‹-6Ù­…0ã·7y<¼\ÖËL¢EµËôi|ônmuœ9 ,[™vT|3|³¬ªŽH§lçÅ€Vù¬™Å2sù®}õŽ#¹<—ÙÑån®åž¿Ö‚&¿kh³’ryÎ×(^í«ö ãÈîÿ:NÅ,+Ûß™ùÌÚ]¥ëéãƒü[œC̺è)ü7&~÷ä]p®X´v€bzc¬„²}¯FÈa;0)×ÁÂÜsœdGQò^Ü…aÇ‘û£ tÎëÓÎg~¾°ÎY'òµG¾êg{6o^[Ë}µGù¬æÙòÏÓãµÏȽ¼xz<ÿ­XQû¬žYñfäÈ®%ײðçƒìq¦”¡0³KÊ6U"?#2¾yr+>//•ü[z´ å!ã¤?sìù}Gþ ï?g×Oe¼P»ªÈV$?ŽÂ«À ´Cz;çyÚ«•<–ý{_ è!³»Z~ZIõ^Øðl¢[ž«æÙõŠaTÿ ²±Þ…^_å¸îŒY׿Gž¨:‘™Ð¨à´-i?š@ZÕ>{y$mf.53~¡KЈì­ä'«GêBrÝ/c*új êIéG̨·™ ÿ½¸²r=f}"‹.ý]–—/™${z²Ç–}¤ßš|–Ü+úÌ&õerîٱ⯀úL‚Ñ+ŒžE ãcŽLû»wõÊ º(HÊ·Hqf§¼êòN‚çCuÁ@¶ª(£'£W_EYcäõØ1~=‹ÀNfúv‡X?™NîXr'—}¼]ÁÂÚmµ ­Ç»,òüÏèGí5™Éí÷&av‚Zþ·¸,ÙÝÚóÝÓùX¹â‘¶ªcƒôèï¨ý,=wf@óÉ=Ûä"Fwf²–ÊÂ÷ÞÏãyc‘‘ûÁ:#„BvÁ—/çq‹€õq§Ý«úWõõ<×rÞ—Ý›WOÏÏR=E;:Qf¯Ò«Oçh·ß½œ3Ûßíÿ¬xªã_ÉÅN—s3 ~Ó`õyÒàGíŸLϸèFç2~Èãì&cß´”õ¼H`9j½ ½…‡ô _‘ž¬¯H¿å§%³ÎÉ6=/ æÍk|õ|’/€é¾ZÖŽ{ä:öLûJ\3ðj͊њЙ±´Œ¯ͼsÕ"Û­b½ÇU?{õdVkO_Ö~äÏnyÖëØÒéÅÙë›Ö¡ñüõâˆúë󞟞þJ¼Y?5áßœçÚ$c…Þ]ú=›íÓÛqŸDtõ‘ym5£ßÒÓ£ÛÒÑã—å§´!eò8Z`¬»¢ªêÉÆó¿¿Ô†-G¼ª`ÙŠÚëï‘í—~–M”Y(Wø6Š7^«7ŠÊÂ0ºXdA Œ®UkNUç§Ç:ï-T’ð€ ­l#‹@$oº½@-=hÇÒŤ¤Çw`­Ë[D-{‹ÙDùì×Òbô|ÍÄY­7T·‘™dã²Ú!ÿ­üFúóÏè¸Iï*ÿ®²³šlVáeú_§QûÞ&tul;øs0s7G—$#¬\ͯÜ5žŒwu°Úæ¨-ÔŸãO!;È< ÈÈ®º}Éúyµþ—÷+ó¿:#}È"z&¿õ®R˜§`—ŸY_gë&çù÷¸*Ïw¢§>—:sµ³™µc‘÷2óê¨Zo•öU?µn÷E YDë‘?éYòvýL2*G?CYþX9xšåUö‰~6œ)·@~î–K+5åéóê-[‹ˆªŸ%zvJkűVA)Cý2úgÉ‘?«ý˜­¯7®]rÄ»V›Sáùøt>í ZV:ƒ~&l¶µ«åè;™‹+}Bµaµ¹Z.}ë¹’ÓŸŸH‰4j%F;¥E}žÄêÁ]w™¸]K;}ªØDâÕr=«›žŽsøÿ´V#´€Tì ö«å¨`fÉwp§…øN ÒqävPÔ~—ÜÚ`{õKà-€V´¢¨¥ÞhåZ-÷¾“¹\u€°|°êäJyóm°Ù¸4ÿûÏAeÇêY+”×·é¿Û®­¾­í,9ÊÛ,9²§û´¶«å²¦d­¶ÛªÍ+åz u¼‘ÜÒ ™9Aå 7Cß*îî![ù¦ q§+B!„½<åv²Nþ/¢2ØQÛÌSôòþÜzâ=êÒ?‹y³úÌŽå.ÏIÐXéqS]kž½^Ý—æÓsÂK²§k†~ØŒžzü¬²#oY›‘o «oÔgËŸe7òÍÊû¿ºz Ê3<*¯ús¥Ü+XÙf‡?¨Íh{Ô7ÓßÊ…î“í‹|Òú£v‘ÿ›:/Çž–Þ“TÝ~ä¾Nöõ®Ur8šç»y¿QƒhEO|­ ©?36ÐñyþýwÒš¬G1z Ÿ|¬ŸvÐÏ›™¼ŒøMHŠLÁeÎe€+¹ˆôTuWʨ!#üVwJkÇ~C1ΞXÑâÑkgÖ• !Ç‘¼Ÿš:/?ïHïe¼‡~#¬µýa²Ÿð)ÄžI?Úÿ©|S¬äƼñ3‹÷Êg³n Þ4Nd?á-À·ݪÈÛ„ ½õãD'+xý0:¡¢vÙgzñˆž™då„$ò× ýKÆ [+å„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„òü—EËîâ±IEND®B`‚CubicSDR-0.2.3/font/vera_sans_mono16.bmfc000066400000000000000000000020101322677621400200740ustar00rootroot00000000000000# AngelCode Bitmap Font Generator configuration file fileVersion=1 # font settings fontName=Bitstream Vera Sans Mono fontFile=VeraMono.ttf charSet=0 fontSize=16 aa=1 scaleH=100 useSmoothing=1 isBold=0 isItalic=0 useUnicode=1 disableBoxChars=1 outputInvalidCharGlyph=0 dontIncludeKerningPairs=0 useHinting=1 renderFromOutline=0 useClearType=0 # character alignment paddingDown=0 paddingUp=0 paddingRight=0 paddingLeft=0 spacingHoriz=1 spacingVert=1 useFixedHeight=1 forceZero=0 # output file outWidth=256 outHeight=128 outBitDepth=32 fontDescFormat=0 fourChnlPacked=0 textureFormat=png textureCompression=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 invA=0 invR=0 invG=0 invB=0 # outline outlineThickness=0 # selected chars chars=32-126,160-255,262-263,268-269,273,286-287,304-305,321-322,338-339,350-353,376,381-382,402,710-711 chars=728-733,937,960,8211-8212,8216-8218,8220-8222,8224-8226,8230,8240,8249-8250,8364,8482,8706,8710 chars=8719,8721-8722,8725,8729-8730,8734,8747,8776,8800,8804-8805,9674 # imported icon images CubicSDR-0.2.3/font/vera_sans_mono16.fnt000066400000000000000000000705321322677621400177720ustar00rootroot00000000000000info face="Bitstream Vera Sans Mono" size=16 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 common lineHeight=16 base=13 scaleW=256 scaleH=128 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 page id=0 file="vera_sans_mono16_0.png" chars count=254 char id=32 x=212 y=102 width=3 height=16 xoffset=-1 yoffset=0 xadvance=8 page=0 chnl=15 char id=33 x=254 y=0 width=1 height=16 xoffset=4 yoffset=0 xadvance=8 page=0 chnl=15 char id=34 x=204 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=35 x=68 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=36 x=88 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=37 x=86 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=38 x=168 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=39 x=252 y=102 width=1 height=16 xoffset=4 yoffset=0 xadvance=8 page=0 chnl=15 char id=40 x=224 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=41 x=216 y=102 width=3 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=42 x=40 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=43 x=64 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=44 x=192 y=102 width=3 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=45 x=111 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=46 x=232 y=102 width=2 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=47 x=241 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=48 x=144 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=49 x=42 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=50 x=176 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=51 x=192 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=52 x=200 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=53 x=216 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=54 x=224 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=55 x=230 y=0 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=56 x=238 y=0 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=57 x=246 y=0 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=58 x=235 y=102 width=2 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=59 x=160 y=102 width=3 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=60 x=16 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=61 x=24 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=62 x=32 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=63 x=18 y=102 width=5 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=64 x=48 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=65 x=56 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=66 x=64 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=67 x=72 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=68 x=80 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=69 x=88 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=70 x=96 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=71 x=104 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=72 x=112 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=73 x=6 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=74 x=248 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=75 x=136 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=76 x=144 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=77 x=152 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=78 x=160 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=79 x=168 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=80 x=176 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=81 x=184 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=82 x=77 y=0 width=8 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=83 x=192 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=84 x=200 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=85 x=208 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=86 x=216 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=87 x=0 y=0 width=10 height=16 xoffset=-1 yoffset=0 xadvance=8 page=0 chnl=15 char id=88 x=224 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=89 x=232 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=90 x=240 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=91 x=228 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=92 x=0 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=93 x=220 y=102 width=3 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=94 x=48 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=95 x=24 y=34 width=7 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=96 x=208 y=102 width=3 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=97 x=14 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=98 x=28 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=99 x=42 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=100 x=49 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=101 x=63 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=102 x=70 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=103 x=77 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=104 x=84 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=105 x=104 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=106 x=106 y=102 width=4 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=107 x=112 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=108 x=128 y=34 width=7 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=109 x=136 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=110 x=119 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=111 x=126 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=112 x=133 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=113 x=140 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=114 x=48 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=115 x=147 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=116 x=161 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=117 x=182 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=118 x=196 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=119 x=140 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=120 x=203 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=121 x=210 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=122 x=217 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=123 x=84 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=124 x=250 y=102 width=1 height=16 xoffset=4 yoffset=0 xadvance=8 page=0 chnl=15 char id=125 x=36 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=126 x=16 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=160 x=184 y=102 width=3 height=16 xoffset=-1 yoffset=0 xadvance=8 page=0 chnl=15 char id=161 x=244 y=102 width=1 height=16 xoffset=4 yoffset=0 xadvance=8 page=0 chnl=15 char id=162 x=224 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=163 x=48 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=164 x=56 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=165 x=136 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=166 x=246 y=102 width=1 height=16 xoffset=4 yoffset=0 xadvance=8 page=0 chnl=15 char id=167 x=231 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=168 x=172 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=169 x=95 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=170 x=60 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=171 x=238 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=172 x=72 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=173 x=96 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=174 x=122 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=175 x=136 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=176 x=131 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=177 x=144 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=178 x=126 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=179 x=121 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=180 x=200 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=181 x=184 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=182 x=192 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=183 x=238 y=102 width=2 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=184 x=196 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=185 x=252 y=85 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=186 x=30 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=187 x=245 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=188 x=149 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=189 x=0 y=34 width=7 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=190 x=113 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=191 x=54 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=192 x=32 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=193 x=240 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=194 x=232 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=195 x=224 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=196 x=208 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=197 x=200 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=198 x=131 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=199 x=160 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=200 x=152 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=201 x=120 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=202 x=96 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=203 x=80 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=204 x=90 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=205 x=78 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=206 x=24 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=207 x=0 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=208 x=104 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=209 x=40 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=210 x=128 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=211 x=24 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=212 x=8 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=213 x=0 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=214 x=248 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=215 x=31 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=216 x=11 y=0 width=9 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=217 x=112 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=218 x=32 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=219 x=16 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=220 x=8 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=221 x=248 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=222 x=120 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=223 x=24 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=224 x=38 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=225 x=45 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=226 x=52 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=227 x=59 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=228 x=66 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=229 x=73 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=230 x=80 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=231 x=87 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=232 x=94 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=233 x=101 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=234 x=108 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=235 x=115 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=236 x=8 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=237 x=0 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=238 x=168 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=239 x=120 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=240 x=122 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=241 x=129 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=242 x=136 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=243 x=143 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=244 x=150 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=245 x=157 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=246 x=164 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=247 x=50 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=248 x=41 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=249 x=171 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=250 x=178 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=251 x=185 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=252 x=192 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=253 x=199 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=254 x=206 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=255 x=213 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=262 x=40 y=17 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=263 x=220 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=268 x=216 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=269 x=227 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=273 x=176 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=286 x=64 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=287 x=234 y=68 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=304 x=12 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=305 x=56 y=34 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=321 x=158 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=322 x=72 y=51 width=7 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=338 x=80 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=339 x=7 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=350 x=88 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=351 x=21 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=352 x=96 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=353 x=35 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=376 x=104 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=381 x=112 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=382 x=56 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=402 x=167 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=710 x=141 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=711 x=146 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=728 x=151 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=729 x=248 y=102 width=1 height=16 xoffset=4 yoffset=0 xadvance=8 page=0 chnl=15 char id=730 x=101 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=731 x=164 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=732 x=66 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=733 x=72 y=102 width=5 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=937 x=128 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=960 x=176 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8211 x=59 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8212 x=185 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8216 x=156 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=8217 x=168 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=8218 x=188 y=102 width=3 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=8220 x=91 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8221 x=98 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8222 x=105 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8224 x=152 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8225 x=160 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8226 x=116 y=102 width=4 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=8230 x=194 y=0 width=8 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8240 x=21 y=0 width=9 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8249 x=176 y=102 width=3 height=16 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15 char id=8250 x=180 y=102 width=3 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=8364 x=184 y=51 width=7 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8482 x=203 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8706 x=154 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8710 x=31 y=0 width=9 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8719 x=168 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8721 x=175 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8722 x=208 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8725 x=189 y=85 width=6 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8729 x=241 y=102 width=2 height=16 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=15 char id=8730 x=212 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8734 x=221 y=0 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15 char id=8747 x=232 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8776 x=240 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8800 x=248 y=51 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8804 x=0 y=68 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=8805 x=8 y=68 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 char id=9674 x=16 y=68 width=7 height=16 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=15 CubicSDR-0.2.3/font/vera_sans_mono16_0.png000066400000000000000000000177661322677621400202200ustar00rootroot00000000000000‰PNG  IHDR€äµ· ½IDATxœí]=Enñ# K`G¯X"ÀEæ„iø› nH€†0Dc!& Z"3%{$’±œ C¶Y¿ÁÞã«««êêžÝ½s?ÉíMOuWUwWWÕ8—‘‘‘‘‘‘±Š¢(¼÷þÔ|p|pj2æÃ_`¿ßïw»ÝîÔü8wÉÓÒùö}߯•÷šøòË/¿üóÏ?ÿ<5TUUµmÛj/´mÛVUUÑgeY–ã8ŽtmÛ¶»Ýn‡÷ªªªöà´꺮½÷¾®ëz^•–C×u]Y–¥s‡çžaDZiš†ÒNi ´¬a>Éï\à‡~øá³Ï>ûlNYÎ9·Ûív¼Ž©îÎ;w¬wRólš¦ù÷ßÿE}Sh—o ŒWŒÝí½{÷îýú믿N)‹b¿ßïñNÛ¶í~¿ßÓyHó éã8Žt¾ã·(Š‚fÊ1ŽãXEÿ·Ûív†Ï6›ÍfÇq†ïì÷ûý桼y9à1ïûý~_EQ–eÙ÷}ç¼³7›Í¦ëºŽNÌØ6-:¸ëºn³Ùlè;´¼Íf³iš¦áíú°‰)Ë9ç†a¶Ûívî*kѧæß÷}¿Ýn·sxš ¾`ì[uÇqœ[vÛ¶-š¾ï{¾ìv»Æi{ÌÓ¢( mAsÃ0 Ò@(Š¢àfBééÑQ…´Âo6› •r/¨ $ZwéÝ)+–Ä:­¹Í_¢¡ÏcÒh›Ôu]ƒ6T^Ó4 —øÛív;·,ð®•Ë (Š‚ò åz€ Ò`å™ I`Lj4Ö.;|¡¶Æ –Þu]w-}·Ûí00ú €q.e°*âÿa¾âÛߪªª®ëº¶m[i¶mÛÒN“αMÓ4`|Òÿy#Líü˜#¤#Àv»Ýv]ס­ñ›©¼?l+iݤò¤ (‹·3/«,ËR$hÛ¶å èø EQmÛ¶1+d(?ž¶ÆäçùJ¿7›Í¦®ëšîÄx›•eYbll·Û-„³$Œitâ;Nçó8ÔwÎ)Âvúx‰qNHÏ£¼ÀÍf³iÛ¶íû¾·ÎÀ eY–Þ_=çã8ŽhL˜%¶U¨îÃ\Ø^iRƒœwòÁ@Ò$¤½3ŽãÒ¶mKwo\xQ­4Í—.RÙtQÀ6tüŒ Hüàý~¿çú·4¸%,ô}ß·mÛR¡ÊÇé8Ž#&þpÍf³Ùn·[iLkÇt=Ú¾Ú@êsG`@·ÀÒ{´óy†¡Õž“Ìc%Ô—) ’Ѐ± ÿµ²´ÝÈn·ÛÑŽêü·ôŽVè`¬ö´LÚñ¸’€£ÇA©Ü¢( ´Â}ß÷t°YüTUU¥ô v‘Zú¹–.®å-4è;üèÌÇšWb®‰åô}ß7 |àƒD+으ýÕ´â$¨ ¶©Z£Y4„!JÇ$ååBW·y]×u ‘ê¿Ô`†ï֠ɧíLjÐ!в°+àmÂ=?ŽÄò“Ò\Ã1ý¾4P¾3rîêm¨í¬…8öy(/çÜåÄ+/€í$+üœ».å$˜¦ã@!-Ê ªPeævl]×5œ¤y–òÖ¶o€Tÿ¥•€”>ábÊ⫬ֆ8b!àú™~Bùke†Ž’sò^8*Ic‚Ÿùéÿ|¬iGè)@:†^aÊûƒR“PÚÂãÚƒn=pm„wb€¶Úó]žQÁÓu]§­slR>ZÞ!}‡&c®æøD–®9ŸVY|«©Õ³®ë÷ÊUUUÚbañ“ÒGÖà\@ÇŒö;ûý~ŒB‹ ô Ú€§5MÓ˜·´0©`(ˆ¤³}/öÀ•äОcðî÷ëZ¼Ñ-™4Ñ´QUU%›&bÀÛ$T÷Ýn·ÓŽ*¨¢•—Ë… ¼ººÕø õ½Ô!Þy:,cêMy ýNEß÷ýÃ6K2’Œõ(í¤:ÅÜÕ®S•{3™n?Ò•gFF†SO~Ž©üð;ðŒŒŒ÷Ø1hǪŒŒŒŒŒ÷ ¸c>5· ’•^Æíƒ9w´ûÜЕÛ1! e,ƒÜ¶·fÿZnÁ0]…»ŒŒŒÓ¢ïû^òò$›øc²= é’¤ƒí9¿WåWTÒi¨|J;Žã(Ý·jˆI)_j/š¿Õ¶0æAÞbàÆKÈPŠóÁÔÞ{Ù‚ækñÇû€ÛI„è;ì|©M 5áŽé?­ÞÎ]·­pîª+õ¥ü M$[jobñgÑßÀåÿâ ÿà ÿKžym{ðLŒ1ˆ¨ëº¦ *ŸÒG[\åspK;­|)_>(¸€ã^¸e'ÏŸNþØúÑI€ÁOù 9C¥ò‡¶A-zÉYK[줺ZíO½+º®k)ÉÆþ¨ß™sSqêEÏ¥†T™P:GUUU¬Ri†!díd9HÒL:Î`ñçV¦:¡€p®Øív»Ôòésž·P ®»àE‹ À' ÿÞLºÑ07¦üIŽgHOáoIúPžÖ3ú\òä<Õu]£¥þ¦v!”ÇJ4ýœÁZEˆ=tÅ’¶&T˜À®æ½\Ï@¥¡Ú˜vÜÓŒºóèGRhh%«|‰žJõ¹`Jùô¹•®é}ðïI“?†ZÌœ?¾ Håojý¬ÕTÊÓzÆŸów$øe„âпR}¢é§ VH8ïgï©1Û¶Ûí¶m‘褶x¢ —\ê÷ìÜAêaËÅ%¬$˜è¤›:é À˜t«ü)ô)éèi¢Ä¶Ž`|åÅ_,Sø£ÏùÀ¢‡n;“0ò²òwîª#Óv»ÝJÇ ŒYM×4Mãýa·¤ñ—þ©ô“»÷W'Ì\`õL™Öoªà´|r¯ª)mBŸO¥¥Ï™àüCÈ·)ŽQ<¡Ö¸`§ôTA—R? ®Œ™ `²ÛívÃ0 ’âNË+&ÿÐ@@†½Bm­ÅzAJ·v¼ƒ(è{Ú e ú­•mu๦N®LÙbKÊÕ©õ“ø‡¶ž>ŸzàïÅòåœ|ŽÖhb@èêÜÕñoZY’¢/…>ú…P:×Ä5MÓ„t–Œ6˜t°”0VNQÂ-©ÔaR@(ÿT%¿‚]R ª$œ«ÄÓ觬–Ò3K@Ñÿµ ­T¿Š ¬Õu†äšØôÑ/˜\`É[çÂ×`4x‡¤´®axp B#önk@(nxùK €)×lT‘´´@Üs'ÝÔþIÉkbò´¾Hý?·L^r|âó<,o=8óHÛp«~ýœtË’òÎY„F8wÕœX2C­ –­ÿTþꢉw©-zL;…ø_[xˆËïýálŠ•266€U¾p%&œa2Mß·xªl €¶mÛ• fëS¿ýÜtË—âÎY8§ŒhÛë_á|£ êQJ=I”€3BôsÓÍù ã(9¼?œ¥æz›­!¤€)h´-Ú’€Ò ðIŠ ÄÓMÖ™ÒS]PhLá“§Ñwè „8§áˆ­ŸE?7=6^ÁÐmTLPÅä"ÞÓ"¾ð€)h¬€SòŒyw© :7bŽ•¿6™–jK Kc8wXí¨¶›ŸySZþJ:é±Ã 8cÕÏ¢Ÿ›n)?˜úä“O>yóæÍçœ{ýúõk)àÁëׯ_Ǥ[H¥ÿí·ß~ûâ‹/¾ˆÉ;?þøãŸþùçüšf©ü¥®4MÓ }çbnûSüóÏ?ÿÛúÿûßÿ>|øP»&û믿þúî»ï¾ƒøûï¿ÿþøã?þå—_~qι—/_¾üú믿-ý Ð ˜jøäÉ“'¯^½zEŸ½zõêÕ“'Ož8çÜýû÷ïKðþýû÷cêgÑÏMþüùó§OŸ>¥×€OŸ>}úüùóçb…¥k4+àAL@„Bô’´æ#B¶G|› ¸17p‚±ècø·ÚÏ¢§íÀ nè{Òï%øw.l(CuرÑs®u HéñÑŒX“^+ Œ•nÕonþ1åGáÅØ­{F:¤I—qô#K µÍ%Á÷ÞÀZ!2æ!·i–l§Üæ'Fì0#[ä%½ó8ËÈÈÈa©€´€·!`Ä̰D<Œ÷ÞÏH`áÜVœ–€µ”SKèsîÿOÍGÆ{ˆ¬·8-ðAMú‘Í÷X¤Ý1[÷„Ñ÷ˆóÇ;Þ뜳:ð»_+¶ŸV>µ#—„EÓ4 8Ñu]Gr(ÝúpD¬’5´ƒ(Ë÷ðÐ?ô¾ëºNr>¡;ÁØñaõ_¨}7à»éÃÒ5"zøø€i6½£×®!,e!ˆéçâÒl·Ûíâáð¬€Ñ&æh à âÜåó§§´k'hÇêz,u,Ò©9+uذҥ?Z¶€±KL?íoš?ÿ¦<ÿïszi|L!@@Rþ¸û0&?xÂ;”žæ¹ÛívÒq=R€õÎ~¿ß7MÓÐ:HýGÉÌAÓ4Í;ËDËV8: €‚Xú¿5õÖ’: ô’ÓEÛ^~x‘æ/€§Ã^²E—è—ôÔ_~xTVs^†¶òtÝå·ªª*Ú?Ôò®ïûžšµÆÐÇÖUKñ“—ä›BïÜU¡º¤ÐbWHÀN(¶l eY–WæÊ©}œ³Ä­¡±=¥ôÚ65f€ŽãÕ/±R‰ÞZ­­tç®®ºš ‹ÉOãOË4?êà=Ξ«mAo¥[üKàŸÛæù†ÎÏP†Ó€’*°µ÷þúñO2åÕ0ÅJ×l_­2´ƒBé1 XÏC b!~誥}xÑʃGš‘h¤†n£mÛ~í”ÞJ».d&KÃÅÖ/¶iD í=ËÝ3sÓ¾âÇÐÑ~ã;(~¤I]×u´O±CÞ‚ð@Pq_Î#†¯QÃÂXÒˆú¹€2@ϲÚç·¥r­€1¼ŠÒM¡ MZž+0C>ª­•n)Sù ¥k4\/‘’§¤„J ’2W ˜2Á´úCy¬]?N‰üꟹ“Ÿê(´y[k†0VçÓ©¯i¼5@Š»fAë”%o¤8T+/Ñó€ Øâ¡ã­t‹?º]µ ÕNžK¸DÌk@)=Æ~í̯yû[mš"xþ˜à  u(Š¢˜b å^}JC Ø€ RV¹f86’ UhçZé¼®©õOI—þ·Êwn]C ¹õ`DäýuC C¢”zPP$ï:¾ÓöÓ”€0XŠ-7ã ±t°‹›†) 〦iš÷Ý úV òŒ`÷°¸ pÆqc¼ËÈÈÈÈȸI€v_ë°tý´:ÀË uà‚Øj—sé‡5x±Ê“òŒ¡CtÎ…ÇD(-ô 4 ëxpM¥:꺮Cg.¾Uéû¾Çû(LbŠÚqK6Ýt7Á½øÎ%­ªªŠê?¨'](w˜¡ößk¤!î ­”i\XÐúq%¥;vÚ±÷¡>*¼Í(Ÿ!ºÐ˜¥Iý'ñãGsŽNzïöÊôÜΟSaÐ4M#yûñN“Îh,H{ël’\§N£ÿÓD;ãQ)Br­4Úþôœèý!,¼T¾ «Ù±ÓŽ-œ»ºpÑ3»÷Þkí¢³ÆD(—'ñ>Žã¨-ÖRýÞ™â\Zé뺮ûþàPAwô0-¢&6›«_ìÎ-òÏÓ¤¢¢[#M9MNsî°‹¢“^zg.ÝÔ4þ,ÖMŸÒ}`½ÌÁ·ò/_¾|)”xùòåKúìñãÇþùçŸ>|ø[”ªªªgÏž=ûùÏž={!ðÍ7ß|óöíÛ·w×9¥}õÕW_}ûí·ß:眔Âk¦e¤ã÷ßÿýÑ£Gʲ,ß¼yófmº9xôèÑ£?þøãdÂ9G箇à¦R($¹¸¶’ž8ÝòœcšTÏ”-ݱÓb·Öç|(ŠËÏØ­‘ÀE›ÞœxýÀ]Ø%ºµŽ4È?Vkï]y!E èÜa"ó€Hã©…þæÇqDÃ!ˆ”ç’†àÞ´Â΄¦s²R‡+ƒÐn8f¬•Æ•HÃ0 1ʵsRöýÕø|T¶F;€ÞëJÀ]̘Ò,% X|R¾®ñĤƒQdJ…èi>ã¾ 4eY–ÈëœÒ`\S×uw¤Rxý‹E”†Kÿ5Ò¦\¯…èŽvì¾¥,µkÀsá1J£×€Ð=…Êáùy¿¢7*&~vwÕñ¾¹©,\%!C·$0éµwG$©vÍ8 fàdœúþúÇhbúQ¢›j‹B·ò!ÝÏQ‘W79 ÈÍ„÷‡+réùº9€/€÷×­<32222222âqvF"}ß÷?ýôÓOÎ pîÞ½{÷óÌÈȘëì4'pƒVΜ<é¹+Fñc½C=¹÷#-c©ò22‚8¶1V{:WÂË™šgêÍF¨~›Í!’KUUýMi©Õ[Ðb+¢¼›ÍA‚s—ŠU)-Ü‘çTà¼cáš[?^Æ•<Ž}‡x¬»T9©ù„Þ‡Á ÿíÜåNc³Ùlèï¥ù;%ènJ2œÑ€ö¬¹À\¾¬Ç¯+¬L×.Gµ"‚µ %Å­–0i î=G-Ò¼×-¨Bt(VMã,!´‚8§аèBåiÁ5h<¿a¸ ŽB§t&&Å0%ÐG¨Bu·úqœ»î篵5­ŸTgmkÑQÐñòEàæ¾Ôl™šðr:k,Ѷ†5¢sÁÔuéʵp­4;bTÊûëÁ.œÓƒdPÛy4ŠO4Ú¨!:+XÏSë쪪*ÌC  ¡6– ÛeóúñÀ"KåJ»†/Và[}ðê?ަ¹þ½úØIKŸa›œJð|.Ðt8Ò`ÌÓß¡ t Rÿ ïqh€—ai·CˤæøRºÚš‡$…µ‰¥½/½“â•„<¥$©-åÅ•nÔql¾%^´òЉVKí©úœèCê#êª{ˆWœgá€Ãߟ"œ“°Ø¼è$vîzÀQ¸³®ë /S B!A,jœ?švªT¨Ð+æw¨œ´Õ Ë·.|»GÓpdÀÊAó uL*]Lž8wT’š7o«˜÷—‚÷‡m#ÝúÏåsjZ¨ÿ°uî°jsˆ•·õLê[‹N  Aß“ò.ÀŸkA@m jeí÷û=mC*l»®ë0±éoÊ êFkã\lŒP§s7JþÞfsˆ=AAc©ñ¼¦ÒIôÒ3ìp¤RË36oþì ,ËÑš–àsNý´þãÊû˨R1üZϸ"/†N;_w]×A_! ˆñü9>5ÎwÎÅAÎtpÔÖê#¦€ 4éÓ˜Ú¶[ÊKÛ^†èæKÐhc®@µ e¼¶Ào.”C¼¤{Üâåqìýõ3íTÀuSÒ;<ÐG(0ÎóÒíÀn·ÛáöA:ããõ Î…Ç`h×À•¸”Ÿ©G\qA•€<ØUøð Ò¹‰®PÀÎ – )¦BY¡€Ô)–¿¿„ð~Ó÷¨. ÄËJÀPÿQ#¬®ë:鿉jÀ9¿¡g’—èCS|;wùžkáBƒ/˜¨3 ±cÐûƒÎFZ­¡ä‚nŠ0Ú1Ø‚HÁ.°*@2Ñ@ Ü€¡ë.¿€ü¤+¦m,ïÃÁR®C4@/µ“œaM  ñ¼±bÄð²Ä5 ­{¨ÿ6›Ãy¼¿þÍ@²º´ê Ù±„Ƨsvì|éœÏ'2ÕæSHŠdk J×€eY–ß³±´QÄûcÍŽ™j$4©k —’7}&ÝäXXÓømN^W.0ø‚¸ ¬3ù¬9@Nj‘tΚ€©FBSqSÀTKÀ5Íßç¹uóä6Á¸ÍÁnŠXÊ`)àxsªò3222ŽƒI«Ý›Þ Ã0Äìp$ ®ì.¸¢3#ciÜqî0™ïܹs—, ëº_+yôèÑ£{÷îÝ[2ÿc ïûþîÝ»wû¾ï÷ûýuÀÄÿôÓO?É'öýý~¿ÿþûï¿wî “ùðÃ?œWƒå­.‚«Üæ +MÓ4}ôÑGÒØíº®{ûöíÛÇ?>o·ôzé˜åâj‹[Â",öƒ&Õ¤ò-?lüyú©•ER€é™Vnoá”g{Ø„ޑ̰îA(é*¨mÅZº 5?M‰’úÜråïÅá³âPœÐ”>½cžSqÊÕã=.°Òõýáã©V»’g^è} ë0¥ ¼?V*àð eHåJ<ãwû(‡]‘òCߦxŒ)Z÷Ô6˜ nÜ$•/™þš¡ôT€Ì­çOû«N}N¯Åªªª,WÏu™ô>V–¹€N))ùÅæŸúüv1Ô”×Úy,É („cØ=¤`Š‚o”÷7XZDó½ô@Ö˜úè=¸Jrï) t§b­Ä18¦˜Ã3èø@¤ÖlÇ©üÃî!S€äTz.–Z N*b|—ÔÌr Ù﯆YºI &ÐñïøYüT·KbÉ~ L’ãècܘiZl¹'ÜQF{ß:h6ÑRÅB¦ ’Àv²mÛv @íñcÞ_¢L€ÇHz<ÃdŸË˘+&{¿±2V„~§æ|a­ç!% s—ŽHVEB†aàŽ,|•[Z€gm=…ÀÿÔÔ3ÌåéT°|áÏKþ^HXåG¿°ös <¾œ––ï±V©|êk½?e"òާ´ï›ÑMKbpr›?wÍFz>…ï%W¿µÛm­:Uyk–?×Hš{¡ò´yq£ÆÌš÷ÐÇšÆ+7ª3 pCŸ¦i-T×åI†FK±9•)ðÞŸÆ`iÌñA €y“Á }Ö¨¼<ÍÐèÆL7VdÈÀªcíx“Aã ž¢|ísS$ÎE·ÿ2’úø¦GIEND®B`‚CubicSDR-0.2.3/font/vera_sans_mono18.bmfc000066400000000000000000000020101322677621400200760ustar00rootroot00000000000000# AngelCode Bitmap Font Generator configuration file fileVersion=1 # font settings fontName=Bitstream Vera Sans Mono fontFile=VeraMono.ttf charSet=0 fontSize=18 aa=1 scaleH=100 useSmoothing=1 isBold=0 isItalic=0 useUnicode=1 disableBoxChars=1 outputInvalidCharGlyph=0 dontIncludeKerningPairs=0 useHinting=1 renderFromOutline=0 useClearType=1 # character alignment paddingDown=0 paddingUp=0 paddingRight=0 paddingLeft=0 spacingHoriz=1 spacingVert=1 useFixedHeight=1 forceZero=0 # output file outWidth=256 outHeight=256 outBitDepth=32 fontDescFormat=0 fourChnlPacked=0 textureFormat=png textureCompression=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 invA=0 invR=0 invG=0 invB=0 # outline outlineThickness=0 # selected chars chars=32-126,160-255,262-263,268-269,273,286-287,304-305,321-322,338-339,350-353,376,381-382,402,710-711 chars=728-733,937,960,8211-8212,8216-8218,8220-8222,8224-8226,8230,8240,8249-8250,8364,8482,8706,8710 chars=8719,8721-8722,8725,8729-8730,8734,8747,8776,8800,8804-8805,9674 # imported icon images CubicSDR-0.2.3/font/vera_sans_mono18.fnt000066400000000000000000000705321322677621400177740ustar00rootroot00000000000000info face="Bitstream Vera Sans Mono" size=18 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 common lineHeight=18 base=14 scaleW=256 scaleH=256 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 page id=0 file="vera_sans_mono18_0.png" chars count=254 char id=32 x=219 y=152 width=3 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=33 x=252 y=19 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=34 x=100 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=35 x=36 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=36 x=58 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=37 x=0 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=38 x=0 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=39 x=239 y=152 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=40 x=250 y=38 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=41 x=250 y=57 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=42 x=94 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=43 x=212 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=44 x=189 y=152 width=4 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=45 x=250 y=76 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=46 x=235 y=152 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=47 x=130 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=48 x=10 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=49 x=139 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=50 x=30 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=51 x=40 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=52 x=50 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=53 x=60 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=54 x=70 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=55 x=80 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=56 x=90 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=57 x=100 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=58 x=223 y=152 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=59 x=184 y=152 width=4 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=60 x=130 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=61 x=140 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=62 x=150 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=63 x=64 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=64 x=110 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=65 x=121 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=66 x=170 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=67 x=180 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=68 x=190 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=69 x=200 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=70 x=166 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=71 x=220 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=72 x=230 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=73 x=162 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=74 x=184 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=75 x=10 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=76 x=193 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=77 x=30 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=78 x=40 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=79 x=50 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=80 x=60 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=81 x=70 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=82 x=221 y=0 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=83 x=80 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=84 x=90 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=85 x=100 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=86 x=11 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=87 x=108 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=88 x=33 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=89 x=110 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=90 x=120 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=91 x=194 y=152 width=4 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=92 x=211 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=93 x=214 y=152 width=4 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=94 x=132 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=95 x=170 y=57 width=9 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=96 x=250 y=133 width=5 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=97 x=190 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=98 x=200 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=99 x=247 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=100 x=220 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=101 x=230 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=102 x=0 y=133 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=103 x=0 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=104 x=9 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=105 x=40 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=106 x=72 y=152 width=6 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=107 x=36 y=133 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=108 x=45 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=109 x=199 y=0 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=110 x=54 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=111 x=70 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=112 x=80 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=113 x=90 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=114 x=202 y=133 width=7 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=115 x=72 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=116 x=81 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=117 x=90 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=118 x=140 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=119 x=132 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=120 x=210 y=0 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=121 x=150 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=122 x=99 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=123 x=0 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=124 x=251 y=152 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=125 x=16 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=126 x=200 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=160 x=0 y=171 width=3 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=161 x=4 y=171 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=162 x=238 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=163 x=220 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=164 x=27 y=133 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=165 x=230 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=166 x=231 y=152 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=167 x=170 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=168 x=118 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=169 x=84 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=170 x=8 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=171 x=135 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=172 x=240 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=173 x=250 y=95 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=174 x=120 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=175 x=154 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=176 x=32 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=177 x=0 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=178 x=106 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=179 x=124 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=180 x=148 y=152 width=5 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=181 x=10 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=182 x=20 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=183 x=243 y=152 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=184 x=178 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=185 x=86 y=152 width=6 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=186 x=24 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=187 x=63 y=133 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=188 x=30 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=189 x=40 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=190 x=50 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=191 x=242 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=192 x=155 y=0 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=193 x=144 y=0 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=194 x=99 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=195 x=232 y=0 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=196 x=55 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=197 x=188 y=0 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=198 x=166 y=0 width=10 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=199 x=70 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=200 x=80 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=201 x=90 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=202 x=100 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=203 x=110 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=204 x=48 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=205 x=226 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=206 x=194 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=207 x=218 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=208 x=77 y=19 width=10 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=209 x=120 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=210 x=130 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=211 x=140 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=212 x=150 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=213 x=160 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=214 x=170 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=215 x=157 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=216 x=22 y=19 width=10 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=217 x=180 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=218 x=190 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=219 x=200 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=220 x=210 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=221 x=220 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=222 x=230 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=223 x=160 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=224 x=240 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=225 x=0 y=114 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=226 x=10 y=114 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=227 x=20 y=114 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=228 x=30 y=114 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=229 x=142 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=230 x=243 y=0 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=231 x=76 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=232 x=152 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=233 x=162 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=234 x=172 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=235 x=182 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=236 x=67 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=237 x=112 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=238 x=175 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=239 x=202 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=240 x=192 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=241 x=18 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=242 x=202 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=243 x=222 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=244 x=232 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=245 x=242 y=19 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=246 x=0 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=247 x=20 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=248 x=110 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=249 x=117 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=250 x=126 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=251 x=144 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=252 x=153 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=253 x=120 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=254 x=210 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=255 x=240 y=38 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=262 x=60 y=95 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=263 x=49 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=268 x=0 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=269 x=85 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=273 x=88 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=286 x=20 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=287 x=130 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=304 x=56 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=305 x=103 y=114 width=8 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=321 x=66 y=19 width=10 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=322 x=220 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=338 x=44 y=19 width=10 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=339 x=96 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=350 x=140 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=351 x=229 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=352 x=150 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=353 x=121 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=376 x=160 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=381 x=180 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=382 x=148 y=114 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=402 x=72 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=710 x=136 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=711 x=142 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=728 x=93 y=152 width=6 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=729 x=247 y=152 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=730 x=160 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=731 x=166 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=732 x=40 y=152 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=733 x=79 y=152 width=6 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=937 x=210 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=960 x=240 y=57 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8211 x=12 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8212 x=60 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8216 x=199 y=152 width=4 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=8217 x=204 y=152 width=4 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=8218 x=209 y=152 width=4 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=8220 x=178 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8221 x=186 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8222 x=210 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8224 x=10 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8225 x=20 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8226 x=130 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=8230 x=30 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8240 x=48 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8249 x=172 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=8250 x=112 y=152 width=5 height=18 xoffset=2 yoffset=0 xadvance=9 page=0 chnl=15 char id=8364 x=40 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8482 x=177 y=0 width=10 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8706 x=234 y=133 width=7 height=18 xoffset=1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8710 x=24 y=0 width=11 height=18 xoffset=-1 yoffset=0 xadvance=9 page=0 chnl=15 char id=8719 x=50 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8721 x=60 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8722 x=100 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8725 x=108 y=133 width=8 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8729 x=227 y=152 width=3 height=18 xoffset=3 yoffset=0 xadvance=9 page=0 chnl=15 char id=8730 x=110 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8734 x=120 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8747 x=130 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8776 x=160 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8800 x=170 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8804 x=180 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=8805 x=190 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 char id=9674 x=210 y=76 width=9 height=18 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 CubicSDR-0.2.3/font/vera_sans_mono18_0.png000066400000000000000000000362171322677621400202120ustar00rootroot00000000000000‰PNG  IHDR\r¨f IDATxœí]l“×ýÇÑ®&»©ôä"Æ7Íck yiÙ¨Ò6²}171DVþ¨<á‚6¹H„4¶1Ô :ÛSЖJ)LkLŠ@€0!š°£yDk’† äx1“*ã\ÄÔÖ®¦úa~Ýéáœç9_ò¿ÏUbŸç¼þÎÛï|B¤Šhš¦åóù¼ÇãñˆÂx<O>ŸÏƒÁàZ¦%‰DŠÅbÑ(½`0,‹ÅH$QUUšššREYït!È÷x<ÏÒÒÒ’ªªªQXUUÕ¥¥¥¥õêxš¦i‘H$BH)ÝssssµC)Š¢ÌÍÍÍy<­jÏU#!„o¾ùæ›;wîÌår9£°ét:½sçεÓm$r¹\î7ÞxƒBb±X¬êPE¹wïÞ½b±X|ôèÑ# ¾Vhš¦Ý»w( ý7¦ø„µšVÒÏ䥩V¨ªª>zôè‘(ŸÁ`0¸^ue"SfÂÂ’¡ÈeeÓùS§NÊçóyMÓ4ø­¦i/L«×º¼Œò#ÂL™É²…BŽ?~œBêëëë‰DâìÙ³g«9(tÙŽÚÒÒÒ’Éd2¹\.Gÿ ß«ªª 9Ng}}}ýŽ;vÔzZéñxùä:<½+‹Å“'Ož„)-ozšL&“P_0-Ô4MƒF’L&“lz‰DÂív»é/=ÑH¤išÆKSkkk«Ýn·üñÇ'‰Dooo¯¨|Ì„…Žº¸¸¸( #bø ¼ï:;;; !äúõë×gfffx–©T*Ë‹óçÏŸƒÁ û9ÔO¥KSÙ2£gyFíè{x{ô:…·~*ØÁ¼uëÖ-™)¨ÌúŸÅì£"Oà}§(Šrþüùó¯¾ú꫼]nEQ”©©©)º2‚Á`Î'”¨3Ò>‚ðt}Azš›››éï`绹¹¹™M^þDß±yÐkpfÂÒ' Fa͉D"txšÞo¿ér•¤Q´_ÃÛ—0:•)3Çã9yòäIBþgxôÚ!Oör¹\®½½½Ýb±X&'''————@ॗ^zÉb±XÂápØDÙrKžÍf³üãÿ˜Íf³FK £õ? BJÓ\‘å/ÚHîzoC¦®®®În·ÛWVVVèxr¹\îÑ£GèxÝn·{fff&—Ëåâñx<›Íf“Édfgìè”Éd2…B¡¿ýË_þò—ººº:UUU«Õj¥—øbbb¢X,¯_¿~ýw¿ûÝï––––èôBÈåË—/à Ããñx²ÙlÖãñxD3 v™Éd26›Í£l¹a !Äçóù …Bf¤v»Ý¾¼¼¼\ÉÀù Ë<ŸÏçEÓêÅÅÅEØ{!DvúôéÓ++++ýë_ÿzåÊ•+‰D"!j‹PÅb±xáÂ… Û·oßÎ +[f±X,€mG™L&ÃÛ7ÚBÈwQ !Äét:VWWWÙN‡Á³Nv»ÝžH$Ñh4šH$v»Ý.ªxBJëYhlôß,‘H$’L&“2¿Üôƒ‘t:Îl6›m>~üøq6›Í>|øðaCCC»ÌQEÙºuëV½4ñðáÇ„”¦…„òùçŸnµZ­­­­­6›ÍF‡…ôÀÿ}}}}Qz2™L6š|>Ÿ¯¡¡¡Áçóùêêêê¬V«uzzzšK;ØŸYXXXH¥R)Þ4ÕLXÖ8B½ XîÀi€etn½¥@·A0’ô÷¹\.·oß¾}MMMM ƒƒƒƒ¢¸èMÀD"‘ˆÇãqÞ€h¦ÌX ô²Ñè7ßOóô¦:f‰D"zšS6n£ª7U£Gdz-T­% ¤FDQ˜àØãí tžÙé/ÿ¬†ßœ?þ›ÍfcOgÌ„…Ù!Œø}}}}FåfÌ*nÞ¼y“>ñz½^½¥ˆ,ªªªgΜ9 …Bãããã.\¸ ÓöÒétzrrròÀØð²e›¹õõõõìLUt\ý” × ìÚ£ÜBñù|>«Õj¥§r`\¬V«•wãóù|Éd2™N§ÓFëÿµ¤¥¥¥Åè”– Éd2I[`—ËåâufzK%X¯)Š¢œ:uêûŒd2™Ü¶mÛ6˜....nÛ¶m¯œÌ¤‡Žÿ—¿üå/¿ûî»ï––––ÐaNž­̽ˢç™ÄÂ+D½KCìÙ¹H gÝD‘NƒÌåøMö FyÌH¯ìèÀ3\"ƒ*“^^œFP&^¨7Ð×Åif ãÕIˆœb0}©Š½`E#kx·*IŸdÔ”e - Çã¡ û¬Š Ï2¹6҉Л¶±›'› ïÒ¥K—ÀµS¶\@È]Gõ~Kˆþ'›^xÝøõÊW6^6ofÚ‡ FK76ŽJ¤ß@\Èeœ½EQš›››ËyK9ù-wY(ì/zÖ‘€m ¢ÆÌv£Ñ‚^bˆ¦‹ì³d ÆhJ¸2«7b°B?}7áÑ£G@®‰gdÓ+úNT—2ñŠÖ™¢ü²WUEi•Yb®g86¢zgÃéͶd §l|Õ §;0Ê!üé¾^ƒ£ HaTÀzû f ý<£µ%ŒÙl6+³ÅëlÞØQ~+Š_&½l¾¡¡çóùüÜÜܯ|Í”ƒ š¦it{-Y£"*»õ G×h¯KT¢>cddã«f¸§ú Y¶ D•þ\oc.z4Õ3ð=ÁzíÆ6>¶QÊTŠ‘!d݃õÊL´i$ZjȦ—îÀtÑ^„Ùr(Ö°ˆŒ‚¨œÖ+¯~Ê G£WÆÕ~®l8Öl„>úè£p8ž øï~ùôôô4HF5666Z, +E£ªªzâĉ####zwúEQ@ •J¥à}4ŸÿíoûÛ7nÜ€NÂÓ®'„îîî¦¦‘‘‘‘:ŠY@Œ*äÅYAREQ”_|ñÅóçÏŸ¯äy¢¼U;½ë¯¢(ÊíÛ·oRR ¡•jxáý~¿_ÏP¬w¸j‡Ã´Ê ðYµÅjÍ‹ÅbÍÍÍÍ ²²eqqq‘ÖèO§ÓécÇŽóûýþŽŽŽÞH’Q½½½½z ÄoÞ¼y2®(|eYRÅb1PSaßR”Éd2fÞä"+§”Ëår‡>\Éû@½…~y ”«4#Rž‘M/mÙi•XEùßËWʉWqFGGGéºgóù§;°uëÖ­Ð×;¯~èQ6œ,Õ~nÙéã­õpMÓ´l6›Õ[GBܬ‰èlŸý\´#-ë ÚŸÐÛ·­ÍYØõhí ùÙ4“^öùz›€å”ƒ¼56¤W¼£ã‹/^dËi½ÂUs3Ž~Ö†ß$äéµ\>ŸÏ¿ûî»ïêhëÏ»Ñiœœœœ„p…B¡@ç^gNûš××××ÏÏÏϳo¶ ƒÁ€×]{½^o?ÏÕ¹£££ÂíÙ³gϧŸ~úi¥FÀãñxvïÞ½{ß¾}û6›– ùßuévH$¢7êÒ¾ý¢ö§ªªzÿþýûpoÂÒ·\ÙøêëëëÝn·[dyw DoV6‚×®2™L†wûµÚœ={öl"‘HÀkâMÁªÈˆî Ë ªªê÷ûýìç²#”žRN0 ž;wîœ(mfžQÎhÉs·ÝOgó¡'FQ©ƒO%²Yµ‚7rò\—y#¢Ù;úo¿ýöÛô½x6>^ZduÌÌxmWQåí·ß~Û蹕Ìàxiü~`45åÝâcßmÏ>PožN§Ó¡P(d6FÀ(÷ñÇ\I<_ö½ì2 ‰®HËÂÓ p8£» ¬Þmx–––– /²ÆÛìÔ]ú’yVooo¯žÆ7éÏà6$¯Þ.]ºtI¯.é[®²iåAoÉØÑÑÑÁÞ†Íår¹K—.]2û<ÑÀ«_ÐþˆÇãqøl !¥FÔßßßS{˜šÒ?¦ô.—T‚^åÑhš¦íÚµk}í~7nܸAO½y´µµµ­®®®BE± ò[( t…ÂPÁlr¹\nttt”¾ïñx<¯¼òÊ+££££l<¢ëÂ4¼z{ë­·Þšœœœ„)﹄üO{A$ä{ËËËËô”‘Þâå‡Ã‰D"±¼¼¼,Sn„räÈ‘#++++‡:d–Rƒ¶Ùl6:"a Y’Éd’gheñûýþƒõ# Üô±˜©ßÞÞÞ^vY*=¢GÚÈ^Ÿ•AoÊMoƈÂвcz#ÜÉ“'OÒÆ‹w‘wÛÑèú'!Æ×‰éB”öj4ïú0/ÐiÙxy£8oJwîj^0bGEÑ´[¯ìx›q¼ò“²ËN±õ6é²×[ºñâƒçŠÚµìæ#[†ð™ìr}‹)! ‡é)-8`4Bè]cìèèèmPÅb±˜Íf³Y,ËÈÈÈÈ¿ÿýï³pæÌ™3¬@“'Ož¤§—áp8|óæÍ›CCCClƒšœœœ‹‹Åbãããã¢%Ì`ž²²OˆD"‘x<ïìììåƒBzzzzèÑ3>ÿüóÏérfë-‰D’Éd²¯¯¯­7˜ÚBþ`–BÏ 4MÓÆÆÆÆ`6A‹fT ½Qêõz½cccc¼ â3ËËËË¢LoÆñÊT¨ª‘nÑsžºQ?bÓíZ´fŸË 'S¿"¤ODŠ#,´$^¡Áîxggg§L£Ó눬ä•ßï÷ËŒfÉd2É3x¼©šÈ0²º{4P)ÇŽ;&cPzzzzèÝfv­K@Onjttttaª=555߇Ãá0œ®èÍd—lÞaF£¼]h0¡P(4111a´?Á+?Ùv*¢RI7YÊMŸ£ú±EVJ(—Ëå2™LÆår¹ ŒÑš]o€uìÁƒV2â°Çqp …B2£™ËårÑù«Ê«(‘ÄÒÐÐÐP*•JÉ<È ¼}¨·_ÿú׿v»ÝnX7‹$ ²ÙlvÿþýûEuFozBO9 ‘"»“­÷\цÝã€ßðÖâìzNäTdF¢ òif·Ö§¢øÙ²Ò;¶9g±rezñA{‘uÈåC¦>xé6Ú+…£1ª_Ý:1:db¶!±°C4<73ò[„ès±ÓX#&³‡!S¬”™hwœ'’.{ôèÑ£K—.]’-‘ÁYO M ‘f¦ èå—žðê¸Vž€l>DécÛ¿Œß!úÀ¨~Ë5æÈ3‚ìh†˜g#t.™ú +w(C6)•€"úl S¿!ÈSÎÚ1Çzv,¬_AAÊ„Þ5¯Æt&‰Dà÷ì òü@ïZëÝà”½Ï^íç"ôÑI¥kúø×&Ï7pÄ¬× «mdŸ‹ ÎÜžSd'x[<‡ÖÑAäaÅ:|°V±¿9DÐM¼™‡È±C4ã1Š-Q|ì÷,l~@"þ;Yï3=‡+ÞÈ(;s3ʯ™r‘ɇ,fó+_­w÷e='k9®“„ˆ½ßx®Ž‘H$BÿO74Ö“×!ØÆÃ3¬k2ÏU™—=W`£ø o2nœtD/h”5PV¼+ŸF£v¹Ël~eÂU’Y*ɯY ë9Êû]- !:*Z¢ÑTOBIÏõ•÷ EáKݺuëÝÙïÝ»woiii‰5©T*EÇ)êtó>“m¬²ñ‰ˆÇãñìÝ»wïS…Mªc‘7P,•8Éæ×L¹”›Yj‘_lŸ¹¬…à=c‹Þ>‘„R0 úý~ÿÀÀÀOHÓŒäÑÝ»wï:‡ªªªÏçó%‰D¡P(°á>ûì³Ï\.—KÔ(xºº7 ØôÊÆ×ØØØh³Ùll|±X,võêÕ«FÏ­„p8ž ææææ¡¡¡¡ÙÙÙY=)-MÓ´®®®®‘‘‘QÝÂèEû¶C£‘ͯ™r)'²˜É¯Þ2]Zð–¼8@÷‰t:>xðàÁÝ»wïæ]ä¢ËYÔñflúYèð Ò`„肈ؿÿ~¿ßïïëëëÓ»Ö)_:Ng2™Ìž={öìØ±cÇíÛ·o‹Â%“ɤÏçóñ¾58Y‰'ÖHÈÆ·°°°J¥RëqÚ1:::ÚÔÔÔ4555ÕÐÐÐ0888( ¢)¢Î¥išFKŒUå¿à¤\j!ji´FˆXKý_6¿Õ.3ÈäFXvÙ'¸Œ¤¾b±X쫯¾újhhhˆçóù|ô;"dËÂñÄH+lå`åúˆH*‘2‡lÿ`Û,ÝODRi"Ç»rÙRHÖ‚`0̽ÑHv¤ùËËv½rÖƒwu•·ûO‰D"·nݺé >Û¨~ÙïEáy†„½ÂvDú;‡¯¬,º†N—‹žaå‰îpð;3é£G:=¼26cÁñòRŽa¤Š‚ÊŠzÖš½{÷îõûýþ¶¶¶¶ÕÕÕU3•jD.—ËŒŒŒðtõÊ…'ÉdN¶œe+4‡ëëëëWVVV:;;;¥44$JIDAT¤5wùòåËt|.—ËŸŽŽŽ¾þúë¯ÃºszzzÚáp8Ì–ÛÂÂÂB6›Í‚>$+z€8 hØ©jI1šNã›o¾ùæÈÈÈlÄ&“É$ä5îܹs‡}þÙ³gÏ®¬¬¬:tè^: …B¡\%³éSEyñÅ_dˆ¦¦¦¦~ò“Ÿü„n_‘H$2111Ak%²m@Xdìh£‡Ã¬`ˆÅb±8Ng6›ÍB|l»£´á7¯^½z•U=­D»:ÀFÙDU(Oº ÔœZZZZZ[[[­V«•U¥­´ŠTsss³HVž’ѱÛíöÖÖÖÖ#GŽYYYY¡;É;wî4L—Ëå‚¼ŽŒŒŒ¼ùæ›oòük×®]ô€ÀFøýøøø8t0vô&䇚 Ñh4:>>>ΖóÕ«W¯ÒŠÂ´Æ!}jTWWW×ÞÞÞÎ uuuuv»ÝNÖÓÓÓÓ×××W(   Ìž@–¦ÅR’)£Ã–s*ö+8EjšÑô£¡§LÑh4jµZ­<Ë%Ë_|ñE&“ÉØl6+ª'Y&ƒª–d£?øàƒ¾üòË/éïôÊ…–‚¢?× WírÝn·{ÿþýû“Éd’mÉd2ÙÒÒÒ¢(Šÿûßÿ#²Ïçó•;BNOOOÛívû;ï¼óŽÝn·‹„3Ï­%Þ°Ö@©T N§_~ùå—‡Ãh·[UUõĉ'¼^¯W´Ööx<ž'Nœp:Ι™™™J®.«ªª:‡Óétº\.W4ž?þ¼hƒwûöíÛwïÞ½[æ”çĉ'¶oß¾]´Ù¥išæv»Ýõõõõ„ü¯¼iEQvìØ±Ãëõz )õ>xðàA%õ±ù0‹ÌsŸYh_g½#ŽUú‘­ ×^{íµrÒ_IœÍÍÍÍF³ 6L¥^fQEinnnÖ #“¶,jQÞzlä|È}út9ñær¹Ü¾}ûöÉ,yhªn@ê¨ÚñÊî¢####ÿüç?ÿ)šÀ­4ú:)ObL6Üfà_ÿú׿z{{{Eß+JIon|||Ö£SSSS©T*×~7C¸H}uuuuAG©/ž¯\¤ô6ÓétzçÎ;¿ùæ›odÓó 7í๼n† ,požç¿~éÒ¥Kð™l¸ÍÂgŸ}ö™Ëår‰Fž^A.—Ëe2™ŒËårm–p›p8ž ææææ¡¡¡¡ÙÙÙYÞF =üi>,VWWW“ÉdRö4ªê3€…………T*•2 Æf×®]»&&&&ôîÈ„£ï‹G£Ñè|ðo æ(7Üf!N§“ÉdR4Z6666òܶ“ÉdÒáp8 Ì7z¸ÍÂèèèhSSSÓÔÔÔTCCCÃààà Ñob±Xl|||Üív»éü †¢¾¾¾Þáp8dŒÀ¶ãÐ÷÷‹Eó:ipûìæÍ›72‚°n­«««c¯ÖʆcÂáp˜wUWÔXdÃìTMTvëŽR£ã©!ktæmÛ¶m›œœœ¬ä`8Ónû°Ìèèèè0ªë-tÇ }zÚQî}€žžžžúúúúùùùyQv‡ý믿þúàÁƒm6›žU3ÜÇæóù¼QúeüÍ8^Ù­W8BJ³³B¡P`…Uõaqqq±P(Db*,ÇÓßßßÏ.KÙv_,‹ìUç ' ‰D"««««££££Ðp¢Ñh”ÞS ÃÎh©Ñ#l|"eÙp› ††††Xé*‘Ás¹\®L&“·ÑÃ=kÐ3¼h4¥ ¼¢<ý‚‹¥$#FÇ#”£Át[ö&˜Ùi2MOOOLÕ )m(>|ø0»@‡ƒ]~˜­ÐÓYÙøh=;vÝH;ÛȆÛlÄãñ¸Õjµ²g<ƒ§(Šâp8ô B6z¸jÏðj5c”…áõôôôÐßÃ^ý‚B䥿LÏ@Oþ¶Z­V³qðøýïÿû?ÿùϦ_㥇Ãáp …«Í'ßG}ôQCCCÃ'Ÿ|ò |¦išöþûï¿_N¸ÍD:N߸qãÆë¯¿þ:ý9ÌúûûûÙã)zŠºÑñJÄ„üïÍRôÔy½ÂU0Œô¯¦i( ® DJ°¡vëÖ­[ðh¼‹úztÚôvðƒÁ`¯—f#-}™øž7O@º DÞP¶Fe²±ž¨¢Ñõ GˆþE 3ž€¬ü½{÷î=zôhUõ-Ê9ó—5l¡5£Œ™‰Ï fÜ‘ä™b-œ~Œfì¨WîûËEÓ4­šZy²iØŒ^Õ‚^&=ùGAAAAž1`ç\O^HFÐr½aVh.^¼xQïÌ¿ZÒJªªªKKKKÇSí FéD"(j¿ýg=ŸµÚã¦Áè5SŠ¢(ssss§N:¥'»Á`0X •W3§FòH" ó[³x<%ÞsçÎãåMQÄ/4KµêŽÏÖ ít¢×©Í>‹EÏA~®ìsôŽe|K1n«2Î;¬cÙ†:VöxŒµô‚O`ÿ¦Jñx<Úðâ«…8l£Ò{ŒÜFÏ’!‰DØ#$mŒ:°2uGˆ¹ÑÒãùá; ég€hµžÅû­L§”iFqWË@\²u(S†²ƒ¥7kWÉfPy•—&Úífà¡È‹CQŒÕd+…vk5r5¢Z ³4z ´š—‡¬(§=–kd03Pmðýe `0L&“Ix_ž(a{÷îÝK7Z‘^=Û¸Eñ…c§nz™„°ÿøÇ?þÁÆÕÒÒÒ’J¥R ðYø ¼g­®®®þìg?û™Ñ³ôògfzzz.+ñî…ËäÂ@݉ʇõ7röx<žW^yå³ï·ãÝMgŸÇ©áþ†™gy<Ïßþö·¿ÑåóÅ_|a&3Ï’i‡ét:=999I_ 2ú§l}Aºü~¿bbb‚W†zíp !ò’Bë®(ƒ€¨5MÓ¢Ñht```â»|ùòeºPØ{ð4Õ|–Q˜H$ ŽŽŽŽ[·nÝr»Ýî@  §2é‘ £(ŠråÊ•+‰D"iy饗^:zôèQѨ7444ôÕW_}eö… E_a½yóæMÖøúý~?4VMÓ´‰‰‰ ³Ò^²õU Ì0êóÂÈÔÛÆxK™v¸…H4+)´žáŒQ½wÚƒ˜ˆÓétæóù|4êMå*y–L —Ë庻»»+Ñ„3îªûý~¿Ñ–òGM+éøžM6›Í:N'=›`eà6*££££¯¼òÊ+"cåv»Ýssss¼ßÊÔWµÚáE‘“Z¯pÕ&N§ïܹsÇëõzC¡PèÌ™3gªvgzŠI0…˜˜˜í[tuuuÉ/=<gllll```€×aaÚ?====111ñ¬áÇb±ØW_}õýBéìììŸÏ—J¥RSSSSå>CUUõÂ… fgggy#;tþP(zë­·Þêëëë«•xõ ôg----++++"5©jÁ›°ÏFˆê«Zíp‹¬¤Ðz…“ä¢víÚµ‹nDdzwïÞ½„ð×H½½½½f‚̳d¬%—/_¾¼×ûf0+ È~¬œ·âñxÜív»Ÿ‡ÍêM=µÄ‘©6ôrjC]BAAg=ÝJ‘½ú®iâ†TÜD'hš¦ÁÅBJÆ`­Óàñx<'Nœ8±}ûöí›ÁåÙ ˜•7«†lŠnÖ Ñ uQ ]B$ë—vv šUã§žG“2ÓÖ±ãyß!5›‘7«Dže$Í¥79ãÐi7r衇Ø6 em â¢Õ„Œž_ FñWëù27Y¹qêuÿðáÇæ³U"—ËåÞxã7)9–€¯7R[ô®ï¦ÓéôÎ;wêìß|óÍ7;wîÜY‰oüÕ«W¯‚Ïððð0oúL;ô···›‰\.ïûãÇokkkõx<žþþþþT*•ßy¸|6999™N§Ó.—Ë5???úôéÓærº~¨ªªvtttôõõõMMMM]»víZ&“ÉÐõ—‡Dq°ßoK8111a·ÛíËËËË"aÖÏ›¶žðÝÉ“'OŠÂ€§Þ2µÜf Ë(=~¿ßqÒ—(DqA¬…†ïWWWWÛÚÚÚè{Ü0"ñ,³hšÊNõFžto.—Ëý÷¿ÿý/ýfiii)—ËåôFú‰¢(Ê©S§NÑñlÛ¶mýü+W®\á=K”F#è‹-¢Ñ…¾ðÓÙÙÙ¹²²²rúôéÓБÌ\z‚å…×ëõB[0Û&+:o8çr¹ÜãÇû|>Æl¹n†{ø´C{{{;[8ï½÷Þ{‹Å …BýýýýlÃÔ óí·ß~ÛÑÑÑA7îåååeѽhUUÕû÷ïßÏd2™­[·n-G4C/=v»ÝÇ㪪ª™L&3666ÆœŒ<TL}}}ýüüü|( AX3. ²rZ4ÕïÐCÓJ²RV«Õ uòãÿøÇµzž( gΜ9ãt:¡P(ÄÞ‡v‡ÃàÖL—}8C›¦;’™4Øl6Ûþð‡?@{êìììüÅ/~ñ‹jæÓ,쌨±±±Ñf³Ùdoê¦O„”tÏ!Äáp8dÃ|ùå—_Þ¸qãÆÍ›7o‚ØÁüüüü¯~õ«_±ÆFUU5ljD¢?j½ô ONNN~ýõ×_BÈž={öŒÑ³˜:&“É$|¶*>„”Ö±ûöíÛÇ©Êî4ƒ¦iÚØØØ˜×ëõÆŸ0999¹ÖË;úÿððð°×ëõvuuu­å~‹ÕjµŽŽŽŽB½§Óét( ­ÕóéMPQ¾}>Ÿ/ŸÏçãñx\&NS€ÐxøðáÃ|>Ÿ7Æår¹@m&N§3™L¦³³³“cµZ­üñÇv»Ý^©2Qz†‡‡‡@`aaaaûöíÛwïÞ½f få´ª…¦‰å´€ZþÇóá‡~¸gÏž= W®\¹²ŸR=¶µµµÐæÂÂÂB*•J­e …B!“ÉdÖò™(,Áì’’ (&‰DŒÚ Ëš;%“É$ÜœSUUu8žµ =alll¬–QÁ`086666222ÒÚÚÚúõ×_}ãÆt§Z+y&ÀHN ÂTCºKX,{ÿý÷ß¿~ýúõÖÖÖÖ}ûöí;pàÀõp ‡ÃazêßÚÚÚj·Ûí¬”z-©t“¼@© 6-ARf¦š¦i]]]]{öìÙcf@XSj,°‘³¼¼¼ÜÖÖÖväÈ‘#t8ÐÈž Z8GƒÁàlß¾};!„\¿~ýúÀÀÀ€h„3’Ó2 o½¦ªúrZ€Yé.™µ!/L8 D£Ñh猌@¹Ï2bxxxØét:;:::®_¿~ýwÞyçyñ–c×ú°ŒØÛÛÛ;;;;k¶<ÖÔtwwwg2™ ½Éåt:zw>ùä“Oxß‹d•dÈf³ÙÎÎÎÎt:v8޶ÓñN)xrZ„”ö2™LÆív»YƒÇX´ìÕÐÐÐÕjµB9-H“žt—̳dÂáp8ìõz½…B¡N§Ó/¿üòËÿùÏþS‹gFx|||üÂ… 6Êy#G£J¹|ùòex¯…¢(ÊÐÐÐÐøøø8tød2™ìêêê¢g˦„oô$˜dŽqªqÔé`½ô S$«T­ôbNR‰=2¤— lå<ˬ'œ-Œž¯×?1v4ª†' FähEÿv³ºd#:<¯Ò]lY«Ûr²°žŠàõø\kï!Èó‚¦itørgš‚ ‚ ‚ ˆ1°1¦wäRëkž²y&$Áb±XŒuáE6ô1.îš#¡(ŠÒÜÜܼ–Ïd¯‰Ê8~Ðo¢wkŸÚò? zW;‰Dè3lÚßÁŒ¼˜Œ²Fð xwÔéJ’¹¡D;ð¦áô3xδSë™Wm ½|°g»nM+½º‰ît¹°ÇAzåA;ù° â“q€©ïÐñÈ,‰DR_z`ú°ÒFGŽ9òÝwß}Wëw·‹èîîînkkkÖ?=G)Pã™™™™KKªªªçÎ;g&PÏ!¤Ô@ 1^†mDhI7=*1x¬€Ù[˜µJWµø(¨Hð€¾ Dö …B¡·îº»»»·mÛ¶¾W=888Çã~¿ß7ô*ÉT¹hš¦MLLL„B¡t0ZD1‰DX¤jC_E¦•@!‰äxüøñãŽŽŽŽH$©D9 D5 )I¥={öl%ñÕšH$éèèè¸víÚµZ=ƒ—ÿ$t‹Åbv»Ý^•È‚Añí*zCãÎ;wxSP½=£MÀjPé@Æ—ÀÌvÙTéÌÝD£ÓÉJbËÆC×7»ºQ¤ÖyÔâ¢M­.ï˜Y°3³S|£7>h¨ÕÊ<4°µžÖTjd6¯6ò ¯J÷.6Ë€wÂP 6¢0K9`K8[,ËÌÌÌÌýû÷ï³/º£.š$“ɤÅb±ÀT‰÷ûÜÀ6#Š¢(·oß¾Éd2åNQiEâr˜×‚ááááL&“¹}ûöíhˆ+öfÊ­Ãr~ÿý&àôôô´Åb±4666Ò` la`•s-‹…Õhãý~#6°p8Þˆé2‚îüyÍ^Mzzzz2™L†7`=K˜]”ÃN¬V«µÖ›\½=L]]]]¥Ò雑žžžYInž¢Tµ%»Ê‰Ï씿§›þ.ÊÄÄÄ„ÕjµF£Ñ(]HFßovÒétúÝwß}w½Ó±„B¡Ðf›±mX*•šZ‹éÊzS‰SbÖóPžó”Èɇþ¬ÒM@ÑïkéTø¾ŸÀK3ZZZZʉÜáp8ŠÅbq½tÓk ¼OŽ~CR;TUUÝn·{pppÐÌïi•i¨7ZA™÷!„´µµµ­®®®šò⃙'ûòŽ ÌÅžjüf3AŸ›oÔ#Àg({™r§O›ž•YÚZÌyN@€ ‚ ‚ ‚ È÷àF‚dV{&L° ÁÖŽHƒÆÂ Éq01+ ©ÕÅ‚A°R+ÎIfDç´âÁ£>.Àï ¿u¥RÕ]Ýlþ‘õ~'©ÙÝU]^½zõê!‚ - P(êõz=“Éd!$“Édêõz½P(Ú·ÓJ4~ÿý÷ßïîîî†B¡ìs…B¡°»»»{ýúõëõz½®ëº.ûl<»­7·ÏÁý7]×õï¿ÿþûh4µ{®Õí ÞO§YxA3ÒC9u Ð` „ru"“Édè”ýߎP(Ú}…ÃËs™L&C· x‡“²ÒÊv ˜®ë:ä¯^¯×ãñxÜïôAäG¼ùæ›oz½/F‰DÂéÙP(p›7/ωYE9å€&¸1S!‚ §]×uZî뺮ãÌÚNÖ×Ú›=1…B¡ iš&{?ÏÄT( ²¦-·y¥¤æK0;ÑÊTp<›¦i6£“ðÒ¥q*kÚDÖ¬rƒ<Ê è ¼Ô±¥“WNÍ4?zUÁtì¦Ýel÷^¸ç¬ wz Y„¨né6àÆ¼.ûœ_õç%Ÿù|>_,‹ËËËËõz½¾¼¼¼¼´´´T*•J­ü>PDüþ>7ˆd>+§ìdÚ©“‚'ÁË‚vÎ3¡P(i¶²°¡1òøüóÏ?—ùf¯ðÊÊÍàAçÝÍ@㦎h%Cv­wŸWÀ®~ü\Yç/èø²ùÕ¥ß €“ÒG ¶³fá£ËÆ QÙÐíͼ”}ίúóšOV9’m#^¾'Ç›ý}nÑu]g'¹år¹¼»»»ûÆo¼ñôéÓ§¦iš§Zð¢Å°æ"ǘB¡PàUë} Ðt^›¡ññ #ÝéëͰx´ùÈ‹c ”·iš¦lgv[GðŒSûŽ/ê¬^QýÐBƯ¶Âøn½ÁáûZÑvíúVæÃ-ÍžMñfÎ ÇÜÖ# ›W7ÏùQ^òI÷›r¹\v#oܤg7Ç:jÆ÷y§€% \.—áoÙô;r `pppBž;;;K!¹\.ç÷÷ùI.—ËÕëõúââ⢛çþO`M‚ iµÊ´Ð…étm>Ó4MSUU=888¨×ëõt:VEÙØØØ`;¿®ëúÈÈÈÈÅ‹/Öjµ«½ ]¼xñb*•JAƒ«T*•T*•‚%€K—.]b…h#ëÇv\¸pá!?Úñx<~ïÞ½{„Òßßß?444Ä–“LÝÙÍœaÀ´,˪V«U·yåessssuuuÕ4MÓ®3Óu4444Äš« …B¡V«ÕØ:"ä¥â‰D"¡°f¾ÞÞÞÞ`0<>>>~öìÙ3§ü;ùV€@·,ËÊår9ö÷Z­VƒëP8‹¬¸¾¾¾nY–Õ*AuíÚµk¦iš …B¡Ð‚¢(ŠÓº.o¹"“ÉdB¡PèÑ£G~÷»ßý®Öv}d'm¾f-sПèI‹[èòÎ;wLÓ4Ùþï×sÔŸ×|NLLL(Š¢ììììüéOúS¹\.«ªªŽŽŽŽúý}¹\.gY–¥išr;•J¥EQŠÅb1ŸÏçýþ>?)•J¥J¥RqóÜk„œt°ÕÕÕUEQøAÓ4Í4MBa5ÀfÐè@#ÃôôôôÇ>þüùîîîn$‰twww^ ƒ¥R©T©T*››››tƒK&“ÉjµZ=::::888ØÙÙÙ™›››£…ÔÈÈÈH___Ÿ]£ñÏ2F£Çãq™m%ñx<þá‡~(ú¶4ÓÄÈsnƒkP>"ËXqìÖÏ ÌD>¢í$쬜÷nÞú´]G7zöÍúb°u¿Ã·@=?~üø1\sòa ëU¦ÍˬÁ{Úæ”&ä“Þ.ÅΖí¾O¦í:í‘鯼~ng%“ñyaŸwrT¥qò—‘Y:…B¡O>ùäø^ž¿OvB>Ù²â9‘òòÉ“2~9^ŸcŸ•©¿FÒã•™Œ„ÛôœÆ^;h¤<;×y©iÁÚ.­m† ³¶NÔl`f ˜Ý‰|,˲ž={öìÂ… dLÀìŒ2´õ`nnnίo’áþýû÷y3 ÖÄ 3OÚäÎ* v3b'뀴ùò &h™5=BN¬O°œspppððáÇ´……®£çÏŸ??<<< ƒÁÞÞÞ^¨çÍÍÍM¸FÏ.Ø´èï-‹E°¹ýnYxBŸÑh4º¶¶¶F ¾J¥Réééé l6›…íR÷îÝ»Ö^»Ìd2MÓ´™BÈììì,­äÈ(Çn©Õjµ7nܰ,Ë"„Ã0Œµµµ5Ñý"Ÿ¸NÈÉlÍeЧ8À >N§Er­V«}ðÁ@»¦—zªÕjµV«Õ =ú);i³3,!Èô)¯ÏÁ·¹©¿FÒãY····ÌënÓ«Õjµáááaè+Ð!Ä4M³¯¯¯OÔ-ÏNà5v¬É},‹Õë'ëìôÃnf"ÁFÈ+* X,¡à‡‡‡‡í0ÆÇÇÇé†Â:²(Š¢ôööö>yòä v…D™L&³ººº:555å”·f’Éd2ccccï¿ÿþûNë>{{{{år¹ ß y„¼TÀ¬Æs ã5r€®'Úñ …B¡T*•ó?›'UUÕ+W®\qú^XH$ @4tÑË‘H$2888¸³³³óÅ_|A!¯¿þúë"…F×uýèèè(‹Åqïðhç„f_9éCóóóó콚¦it¿9888 ÍÌ„üp?>>>ŠèÖÖÖ(KlÇ&Þ´Ôƒˆ­lˆ …§¨wBì«áû¼èº®ß»wïÞ/ùË_†a°ƒA©T*ƒÁ ¯l6›%„l6›¥¯÷ôôô¸]3åÁÎ"———— yY‡t{’Å0 ê“—OÚì ¿Ñ}J´´æõ9ÀMý5’OÑ“PÑrf£ß·¶¶¶f†A!7oÞ¼éÔ>M¯ÝtÜ.èô"''B~¨x@çbÚ!6SU*•ÊþþþþÄÄÄÄÚÚÚx¥‹ft#˜››››šššZ]]]­×h™dy¿¹U–`P#„÷Þ{ï½t:y³ƒëÐÛÛÛ_|ñÅããããv3|h䬓¥¨~h@y`ׯéïaÑè:bßk×ôŒ•­#B^¶ŸÉÉÉÉ‘‘‘‘ýýý}è˜?ùÉO~BȉRA+½0_ ó}tžwvvvìJ°²°m›öì ˜¦i²–žÛ·oßf•äB¡PeilllŒnGZ ¹|ùòeÃ0 zZÖ:ÖJ`🚚šòÛ"ãUqˆF£Ño¾ùæ·|#À¤‡*Š´¢Á['÷ú\;ò òˆµ¾À$”ç¼ê%=Vy£ï‹À.´º<›£ÀN`z•™°ðf‰4^¶ºeeeeåÒ¥K—ºººº†‡‡‡Á©*õh4ýÉO~òÚÌ“W¯^½KKKK|‰D"qïÞ½{~î€cbbb"›Ífi¸È’C3!'å;111aš¦ùûßÿþ÷ªªªÿ÷ÿ÷vÉ8­òÀ³,@žx&=¨#ÞŒôöíÛ·Ëår¼ÿÙ:"äeûyûí·ßƒÁ'Ož<¥CÓ4­¿¿¿Ÿà =;µQ++++v³Ú’Nsrrr’-›¹¹¹9§Ùê—_~ù%ý?lËÚÚÚÚ*½`kkkklll ”%ÞØM^Þóóóóããããô¬¦ f2™L³ÿFçKZN‚â@[ÇXÙùìÙ³gÇÇÇDzÛ6ih§V<ó³×ç¼ÒÌ|òÊÍKzì@www7Xffffèºc—Z]žÍà5Ö\ ?ø±UÉ-NƒÀ®}Òâ`†¤+õÆÏçóù˜í$“É$]ÑÁ`0¸···÷èÑ£GüãÿH 牉‰‰r¹\†<`ggggEQྠú¡,Á V,‹¢Æ(šµÀØØØØ¯ýë_onnn–J¥’išæ?üÃ?üƒªª*oý– è2¦…œS}ÂÖ8^<XÓã štíííí©ªª‚à‡NûÑG}ôßÿýßÿÍÖM8ƒb³²²²‡Ã2;ÜæCžâÂÎ~¥I³{úÚþþþ>Ý·¡ ÑJ!?ôòþàƒ>èïïïwò~fÉçóù@À¿e1XKÕ4M›¥§öNÊT3}tÜÆS¿/fb¨gž‚ï½^ŸóŠ×ô`Òðª~_'ñ!/g–0XѦ 6pɇ~øa³b¸éÔt>ݬÿ°ï»ï¾ûŽýXÎår9¿·ò5(eš¦i´9 ¶*Š¢Ð[çh`Æ¡¼`}}}”?|yB¬‘í"ó?B¾IÏ©Ž¾úꫯþõ_ÿõ_yu !?ÜO_§Íà~NRÁ`0Hoù‚ú9>>>¾víÚ5¿Òc¡¿ ¤jPREQVWWWér…ŒB:ÁüO¯7ÓŠÝÞ›øÊ-Ð~iÙÉ*Yét:M;‚£õ½{÷© §yˤvÛå¼>ç/éÑåÇó¿9íßG;›B, 6Ô²Ìù¾"Ú:óøñãÇìõfÇ9®×åÃ`º:»}ÑÍ6+Þ·Ël—£·||ÿý÷ß¿ñÆoìî¾<ªÒ)pŒ[à[?ùä“ODÛœœ 8Ñ÷Ñè¼å ÑV(Þ6@¶mðîa×®ÜÖ]^lÞE×Ee%›gQ°Þ»šÙ±éÐÉ'ž# ¨ =}L{·mJow„S›iõ6@Qz¼~ʦçe šÌvlžõú‹lý53Ÿ¼-˧åû:R`3Fg†½Þ̽în÷Oz‰ÿït™ð!”•_ëÿÐHxqØú²ƒW¾gÙ½Æì»hèÎàF¸¹ùYìònñ½ÇŽNTØôì„›/^Ÿ÷;𥬀em6¯¢ïòSE$#YFô½2Š2ûN™xn÷ÐË j²õ×Ì|BNã÷u¬Ð 4+„.‚ §‹7Þxã;wîÜiw>igVëAA³„h_|'Ø… ‚ ˆÏðüAAAAAAq P(vwww¯_¿~]v‡Q«žyÅžKÁ²±[èí º®ë°ÎnùXOBvɹUÛËA¡}ÜJ²Ï@00ؽÄþVðˆù–®FבLzl¬ÙÁÜë7!g:òZ»ó‚ HëyóÍ7ßd¯E£Ñh"‘H´#?l®fDt¥;000àõy^9¶›F¿ y…-MCíÁMXY9­ðLØ23R6‚(‚°üè8`/&³³Êèèèh0 òNºk·&.¤ýØ…?)(2J ï §PË"¥Ô¯ód‘‘'':=™À`¼çd¿Qôm^[Þ–f'ÙéuPo5tèZ»oòR²ÏÐm×ì™ó¾pZ¦‘yÆ©ÊLLMÇÍ™ŽKSlÌmœÝŠÉd2/ÂÁm¥áŸN³8 ·ç-ò²?²íLæ¬^z­V±?[@ô/F: ݯdΩpú6§òt£0<Ùà56½VYèñ@ô·eàö¶ÌØ1ÉI°;×£Ñgdú¡]ùû•Žè;t]×YÅZ¨ЇÅÈœp–‘ñ>:×Ý4 TÄN)Tõzs%§Ht/=(ÙYsd-rHìUáߊ38x3z§²…ïàÕ«]Ùˆ ÓnÉK+FŸ>}ú”wgÝ ¯û‘M+<?~üXTG^ÊÀí3ìàÇ~»àTÞ»»»»vJ2Û'Øgdú¡H.ù‘ŽŒlá•寻£ ÛáÑ*3»–A¦SyöHåýîäh÷;]Ùÿõ_ÿõ_n]»Ñu]oE~[aðC°ûMtÝi€súv7}· ¯eÌü¼rp¬"ìÒóRvƒ±¨ü-k^š…B¡À{_# ,…B¡`wJ«—2pû ÝvÊårY4²éxi'nŸ‘µà±®Ÿé8)úè£fggg !äæÍ›7+•J…—îµk×®™¦iÆb±Øäääd( -,,,(Š¢Øõ/^þEé…B¡P$‰¸-“ .\ „ÍÍÍM¶ŽööööÊår9 {{{{IGÄÌÌÌL½^¯½õÖ[oùõ^B¹råÊBNÊßî>·eàõàøÃLÓ4ú!'2ZQekkkK¶xyF†'Ožû¬JhIŠ¢(¦iš}}}}ÃÃÃÃ^„M¥R©ôôôô$‰„eYV,‹ñ´2ñx<þé§Ÿ~J_ …B¡ññññjµZeó …B©T*e𦹏¸¸×Ù5¯·ß~ûm˲¬ÁÁÁA6Mh;;;;†a‡‡‡‡Š¢("áA gZ ¦Ó´3±Òùbï‹F£Ñµµµ5™zF£Ñ< „©©©)Ù†M/whš¦±¿ëº®CÛ€k±X,V,‹ô5ét: ïö¢ ÔjµÚðððp $“ɤÛçiÖ×××-˲"‘HDTž™L&“N§Ó þ„ÒÛÛÛ ƒ‡‡‡‡ÏŸ?ÞH¾ý¢T*•¶¶¶¶EQþå_þå_EQŠÅb1ŸÏçEÏT*•ÊÍ›7oBÈüüüüâââb,‹±ýËZià¥W«ÕjÕjµJ!©T*%+ã ï²Â~'Ýo½¦Ã#‹Å²Ùl¶X,ƒÁ`ð7¿ùÍo¼¾‹”¥‡>)f€Û2ðú P©T*;;;;ccccN3gHgßî¾FŸAÓ4Íííííf¥C_¶ÔjµZ.—ËÍÎÎÎBYݺuëV8Ëö¦ÁˆN¦»µ_™ ë$Ä3 ÚyNÂÀigz¥Rx\³óŒæ™sìLpìL–NÞ}÷Ýwé<^ÐhyúQìš Z§sZ/¤ËŠ¥Ñ(`²ˆÊ…n;²æM™üÙ™íÖ_yÈš¥!M^=Auz7Ù¥E^{ñbªwë%n'“d–Ÿxåê6Ñ;í¾EÔÙe^Þá»è{EòÇKøñ ”!´mÞ€—åÁFŸáÁ«ÛfäÍnÉïØß´ P–FåååeBÉf³Ù@ ƒA?M.vÌÍÍÍ^P,‹ªªªt>óù|>‰D" GGGGáù‰‰‰‰ãããcV«#„H$QEÍŽ¶€9­R©T>|ø—7Úüïínddd„­\Èht‹‹‹‹áp8üÇ?þñ[[[[0ÛYYYYaŸ7 ÃèîîîPd³Ù,ý~Ó4MžeÈ-t>:::ŠÅb16¼60:::ªªªj†qùòåËp½R©TÞÿý÷-˲ìÒ…z„º„ûE–7íÚ­oŒ“Yþïþîïþþ¦µv'–———é|¨ªªf³ÙìÜÜÜœlþZ˜%ÝO›ÿáýUUUZôèwƒ‰Œ“Aî«V«Õ@ €uµJ¥R™œœœdˉn,¼#.„üX¡ ë¡«««+‡ y©ôÉÔ•Ìz¡èY–R©T ƒÁ@ èîîî!<>>>ÞèÒV¥R©œ?þ<¼sfff†-?0Ë‹ÞåK ^óe†qûöíÛÞ¾¦yܺuë(ªªªwïÞ½+ó]×n§»wïÞÒ©<èåº]r¢0z(Xç&°6666¶¶¶¶è{í`ºùùùù¾¾¾>H“·œEû‚É*X²ÈÊy™gÀ´Í“‡íú<Ð×××g𦹼¼¼ÜL[Bœe‹Ï €×žƒ† ³—Vn7¤M"°F#ò9 X[\eàù °€rA!š¦iGzÆ,ãü"‹>àôçd) g~ô컕»èÙ=]¦´rá¦]³Ê‘,¢Ù l—/_¾l†‹Åb÷ïß¿ïô>Zð€b#ûl+¡ñþã?þã?À!°YýÖ`-˲nܸqÃí¤¬G «hE‘^Ïo»t!IJ,+‘H$d}P2™Læèèè(—ËåÜ´Õ»wïÞ ƒAÙ²òR~•Ûööö¶iš¦È—B”­|‹Å¢è/ ÚJIçÓïthÜúü´Í@›faÆf0žiµÑµgÞ˜‰:uŽõõõuUUÕ¡¡¡!;ó¿vŽ_(v÷°fü'Ož< ‡Ãá®®®.BNÊiddd„* ‘H$R¯×ëÏž={÷=xðàëDG+´9~gëœó`ETtÇ£gß´Y²J í@ m…h•™¬;NÊ mòÖ4Ms3{¨Õjµ‹/^4MÓtûl³I¥R)pÄûàƒ>XZZZjÔÒ!‚6ý»Ù-ÀºŸòÒ‹'ÄÁãß²,KvÀã¥S«Õjo¿ýöÛnòV<žs¥hËÈ Z /A6Г/eàG¹Á2ªÝn#HÇͤ žá9g{Ló¢¶ãW:²²…¥íK„’L&“ÀË5ZÞš¼W ÁÃ@E›©eÿÞÞÞžišæ{ï½÷žùŸ—f+hÜ`ºdTý,ø‹Å";Û„A“5{"ÛvÀ¤úÿøÿ8666¦ë'Gf¦R©›ož«ûå@Ú@!M>t½CyòÌôv ëkBˆ·AŸ6¥6Q¶îÈXJ¥Riiii‰ïw7k´I£Þæ~AÏÆs¹\ŽBnß¾}¬·nݺågzW®\¹¢ªªêf·€‘yU¦mƒÒí5?%¿Þç¥ ü*7ÁþÛßþö·¢ßÝZ™¼CÈ׉E¸ {+J§¼„6m&Ö¯lœÊ­™á]Y܆Cµ;ˆ– ~È7¯°ñ?œúu£aqé÷Ði‰½á嵑rk´¬y2‘7qó’Ž2D¦v¬5‹ÍoÃÇ …µK_WUUÝÞÞÞnfá¤Óé´Ì!—.]ºdw„o#ÀQÆ0ÃYÜÉd2)z¦P(ècj yyTm6›ÍŠž …B¡G=:<<<¼|ùòeß>ùº®ë챩š¦i§u9IUUŽŒ–ÁKx-7/2DQebbbþ…^‰Ún` Ð4M£¯‹¾§Ùí Žˆå¿KÈIÙE"‘ˆeY±ÛJ`be"!„Äb±Øwß}÷]»#¶sœk„ßÿþ÷¿¿qãÆ 8RþððððÑ£G üŸ`š¦Ù×××ÇžCð´L&“3¾‰D‚¾¿¯¯¯ïðððÐàåmfff†“Â;K´Éƒƒƒƒ‡>´[ ”™×çß{ï½÷†‡‡‡kµZÍÏ|µP’Xº»»» Ã0!diii ¬8­,,„R,‹t{6MÓlÆÙõ­B4°x)¯åF÷‡™™™72dpppþ†säE÷z‘o^…B¡………EQÃ0Œîîîn»ïiE{ÛÞÞÞ6MÓìïïïb‡²;>>>~öìÙ3ú7/åæö™[·nÝŠÅb1Ñ8bš¦Éû®VÕi#m´ÝÐÖðZ­VËår9ºx¶Äãñøììì¬eYÖÔÔÔ+¬+•Jexxx˜u+Š¢llllÈš…Däóù<Ì–iA@ÿ•4888Ø ¦šFg)µZ­öôéÓ§~æ©“A`𦹏¸¸ØŽ<ŒŽŽŽªªª†aÐV–J¥Ryÿý÷ß·,˺téÒ¥v·%·|ûí·ßÆb±Øäää¤Ó½^ÊÀË3´ I$‰|>Ÿ§ó!’!„²¾¾¾>222ï‹D"‘ãããcP ÛÅäääd,‹†a¼óÎ;ïЊ:ï{ZÑÞž?þüðððPQ%‰DØß'&&&EQše%uä÷Õ«W¯òÆ‘vLi¬v"ÓÓÓÓ´¢çY€F³µµµåGåh𦹤Ÿ l4ëëëë몪ª7nܸÑH]²T*•JOOO-¼gfffèŠÊ/¿üòK?ÒÑh¹Ñ“ú]`mòb…ñ‹R©T:wîÜ9Úƒö;mñN’Éd’·EûöíÛ· Ã0xK:v> ´ÕFÆg„~/X ÊårùüùóçYù*½ À³¬ ‰ºy&›Ífyñ¬"2???O›_Þ¡´bJ¥R„¼ÜvHˆxM-™L&aÙ£™éœh39«È5ÊÚÚÚša=ë¡×O+wÙl6+ê7í¨Sðfg^ñ2s÷Ãbìíí홦i†Ãá0o Æ­|s+Ct]×ÁbÀ::ûc€Wn¬G:=s´{XYE–0/ã‚—gjµZmxxx˜V¸à·t:æ)í³ìàù@ûàÝ/ë"ã3B_Od]ñd ×mØëP‰nf¹°>!ú0QEó´¬L&“9:::Êår9¸g~ó 0_–Fkhkkkk¢çÀJa×aýH§“§ž™ÜxÎOvkž¢ví¨'~;êüDë­^Ê Ñg¼ÎnikŸË%^dÈ·ß~û--÷èÁY4á𣽹õ‹öæ§õÄ/`(²KÃ~⥂âÈóüðéâÔЃÁ X=/€ÉHÆ«˜†· üØþ{ÌyÚŸL´'·Ðkk·nݺ³¹Ff—íL§UЦÿfîùgŸœ¶ŸÏóœ3‹Åb‘Ýu¢ëº[RY³®zyyyYÔ)eëÔϲ†¢ßÝ”A#Ï"Þcš¦IÇïè j¥h«&l#dö}¨Üxá„…EÒ ;ˆ’Éd’צ ñwi¤ÿȶÑJ¥R9þüy¶oe³Ùì/~ñ‹_ˆòöÕW_}5555E¿ß0 ƒ·u/ŸÏçÙ¼†a€CµS9œ)t]×i-Êi†íZ³t»gÕ ‘tÎëôtÁ³úhYéDËH‡!2—øiBo$v»àU‰û¼ëôôЊÁù ´…F£ "-Æ/ @#Q;1¤u`ž>Z9øÓi>~üøq«÷¿#ΠpJi–Ð,s—tddpài­j;ˆˆœÀp8;°ýÖ­ã(‚´TAAAAÖ@;ࢹAAά“T¡P( € ‚œ)hŸ ¯Ncà„æä0è±²Ud2™ =àÇãñ¸iš&oë­L„?™ôd, l¤@?8kõ{Ö¾× …B¡À¶1¶O¼2°{íýÜ*[ÔPœ~wÊ+š'B~ìåݬ`Q^Þ mV6„q¹\.;¥Ã;«ÎËàõvà”é—² †—øð-N2î+—Ëe§tØïƒ²•O§Õ¯›ú¢Û‚¬,ì´ïmV{fÃÄËÂ†Š–m£n¡ÃÜ{y¾¡PÀ„ü0*òêÁÛQpÚ´q;@iš¦Ñ×UUU¿þúë¯ý U«Õj/^¼HÈIhjÙç¢Ñ“c“‹ÅbqnnnÎé¾l6›ýÙÏ~ö³ÃÃÃCQ:¡P(´°°°°´´´”Íf³ét:­ëº~áÂ… ÕjµêGèÙõõõõþþþþÉÉÉIBI¥R)Ó4M»PÇ—/_¾\.—Ë ² 9|K¹\._¾|ù²Ó}[[[[ýýýý[[[[nÒq¢“ê×-¥R©ôú믿nš¦‹ÅbÿùŸÿùŸN A'}o+Ú³[ ÜøÆÆÆF½~rä=!'G}·:/ôC¾ž!Ó aLÝZhœ´úNÇ«v …B?~ìÔh­šåUQDùÈš(Ýe*SgпdÚ'»Æn¨”:z}Þwá@¼9²‘1( ™Y(ÿÃm:²tBýzUôeßÛ ßÛÌöìUÆòck³Ÿ2S´Õ›wèwÙþ"Th3N¦“€æ)БØòÉd2™WEµŸfG~{óÍ7ßtº' …¹O&Nä—¿üå/ú¤lù".‡f•Ïiª_ƒ°níeÀ:Mßë†F€fawz¤S^u]|Œµk@ A =4S脺m¼2¤… ÆèGδUÛ~ç)ìAQ¬ÏùFhàÅ'çÝ(eŽ÷kNÞÝÝÝàyÿò4·N€lz^»oàÝ/«„B¡Ð;wîˆÚO½^¯¯­­­½ñÆoðžg5Rh_PŸ^vÑΗ~?Û¹xùù ÐïbŸwª3öÛÎÚýnäO'Þßiº÷ÓðÖ•E÷º‘o§áþfËs·õåE`óöƒ6lçÈ/ eÌÄ"íCÖéÄÉÄÏþîfK‹Œ “ú:=󡯳¦?ÊGÖ² [†v>v³»²*¼€-;Þ€Èk7¢ró¢ˆÒ°«/x?Ýá휠hKÀãÇ×ëî×Lú×Y¸ß­üé´ûYÚ= »½ß­|;k÷·RžÓéÙÕ/«LØÉv¹Þé¿váÂ… „¢išÆ û££££X,eDxÿæææ&»sooo¯\.—y? išñŠÅb1™L&ýx¯LþƒÁ`°···—þMöþV”Ïààà !„¬¬¬¬4ú.¡P(‰D"ûûûûôõjµZ ºlB¡PèÑ£G!äõ×_½T*•âñx|vvv¶X,‡‡‡‡ýØ·[©T*====[[[[Ó4Íåååe‘“Ïçó3333Š¢(o½õÖ[¦i𣣣£•J¥Â{/­XÈô¯³r¿[ùÓi÷ŸvÜÊ·³v?ÐIòd["‘HX–er·äààà€UNÿéŸþ韠 §ÓéôÌÌÌL>ŸÏ"HQ%‰DÍx«Ð4M³›½ùM'— β÷—J¥R0 Bã2MÓìëëë ¼€7@×jµZµZ­‚¢D"‘H½^¯?{öì}/@;¥R©´´´´¤išæ´l# hÉccccï¿ÿþû•J¥Çã_ýõׄríÚµk¢g¡ƒe³Ùl,‹Ý¿ÿ¾×|À¬1N§E÷Ðíç¬Ý/Ãi¿ÿ¬ÑiåÿªÖ/LhÊår9‘H$`BBNäíðððp€B( ›Íf6Ðu*ÙBÈüüü¼sl#X–eU«Õj+Òr ÎÍNgeee…V¼¢Ñ“H^;;;;쬸R©TΟ?¾\.—7666t]×çæææffffb±Xì›o¾ù¦‘º£z°2躮olll”ËåòùóçÏ‹fê4·oß¾m†1666ÆsüZ^^^¶,Ëúâ‹/¾PUUÝÞÞÞfó=999‹Åb–eY‰D"A÷©îîînÃ0Œ³|?àVþtÚýÈKÜÊÃÓ~§ÏçóÅb±¨ªª::::*óÌkOž  ™‹Å"(kù|>ß×××GÈMô¬u¡«««+‡Ù÷†B/CÃÂ@ùóºÄÀÎèÁjjjêí·ß~;›ÍfyJ˜—–––J¥RÉéÎÚýnåO§ÝÚq+ßÎÚý@'És :B;S‰¶¦]ì»íäèûDéûå5ÚˆWª(2^É„4w´;'#''1™8ì;À–-úYú[dâˆfxŽ„¼oâÕ‡ÈS^T¿gí~·ò§Óîgi·SŸÛû;Íë¾Ó®·RžËÖíõOÿO;&Ëî „x÷ü–Qq·/Ò)/lŠ L¶0èDô½¢ü×ëíÙ7êTo"Á%úÞ€å„Óο€ü”Ëå2>í}_¯{ÙO+¼¶À¾{````mmmÍÎ üÃ?üÊ“ç± |þù石;ÎÚý„¸—?t¿[yÕi÷ÒYûî;íþfËs·õe׿ϲ™'e¶#Èhì‡îŒÄ›aɈÆßãV pÚÂaw= ð¾Y4s°+ú{ž³K‡žÝ¢ïD«]×uvÖ/ké‘y&“ÉðÊÇï½—¾ÎZ1ÎÒý€[ùÓ ÷wÚ€îE „?°Ø•¹sšïo†é:Áí;ßo` ¤#6bÙ`£®µ³. /nó¡iíS¸§\.—íÞ/c=ò<Çïzó™r“…þ>ø»ÝßÇÅ­Àq[HDv³‹Œç&Œ«(ü¢]ždÈ:Åjf£@e2™Œ]d(ö÷FÖœx1ªÝÔ¯ü½†vº“}Ö4Mó£>úÞÁ~ï[Ú¨ÌÙ ngI¬Àt]Þ¢çÝÄofqÈkæPFpÆ‚—wòÊA&¯²ƒ™×´ýP¼Ö…ÛÁCæ~7ƒo³Bü­·FÛºHÎ6ò^å¦iš^B°·ÄŠÕÉ ¤å&7;¨ØÁ»ß)V3[VÍV¼”¡H€òòã¶üÝZ0œž‘É8»ñbkÃï¢÷»Î>ãV€wzh0{~÷Ýwß}úôéSÞó¢À»¡\dÏáp®íÚåU¶žØ2±{g£fx»tÝ|Ÿ¬y\vú“_æéFÂçúµ ÑHùÚ•›ïå}_Û6–0«8ið­Rì  ¯³÷‹Z1 ¿f›¬£óÃ;öQVðyQÜšéÆÅÖ{®/ç@™Òõ":À‡~¿ÀršÑÐõ(j'²mÚP# €×g ßÓÓÓS©T*ôûóù|>›Íf !dppp®—J¥’ªªj©T*Ñ÷×jµÚ7nX–eŒŒŒ´r]ÒƒÁ»wïÞµ»7F/]ºt‰B²ÙlvnnnŽþýW¿úÕ¯Øoã!*¡P(”J¥R„²¹¹¹ õÒÛÛÛ ƒ†a·oß¾Í~“eYV8wuuuñÞ Ï‹Ò½páÂB)‹Å|>Ÿ§»víÚ5Ó4ÍññññV¶ç\.—³,Ë¢Û¡““““„²¶¶¶V«Õj¹\.×ê|w͈Ÿ^«ÕjÃÃÃÃ@ m%—Ëå~ýë_ÿú4”õå—_~i÷{­V«=}úô©×{œÞÿª"Sn¯ ¯=}úôé;wîÀæææf6›ÍªªªÞ¹sçÎÔÔÔT0 ~üñǃgͤËËËË„’N§Ó´¹4"ÖäÍËËËËpÍi&K&¹\.G‡RŒD"EQ”­­­-v ÛÞÞÞ6MÓ´Phžþü¹ìý~P*•J[[[[ccccvÂjtttTUUÕ4MsqqqÑKZvå/bhhh¨¿¿¿ßKºvå õ½¿¿¿Ïû”7ÞïÏŸ?~xxx(Ûüê*‹Å``wbzzzšVœ@é’}Þ/xË"¢õKÚÁ îeg4nî%ä¤ì––––b±Xì›o¾ùÆIіɯÏž={V¯×ë²eă· å·IÝÎ'§÷ÒÈԱݒ›gü°Êœ–÷z)3-9 °QìfŸ0ã³øÁ`0ØÛÛÛ딼K†h4}ðàÁEQZPŠg ð;ûIDAT¢(^*E–k×®];>>>^XXX),ðM;;;;¬ÕC·³BN¬.Š¢(lºô`xëÖ­[p= …EQì” §:‚v@[q€®®®®p8–m~âÆ Çッƒƒ´âV7V„FÑu]ßØØØ ã™Bˆªªêööö6;¨þíßþíßnooo«ªªÂ5MÓ4^»ws/Ô¥ªªêÁÁÁh‰Èm~íàµQ;DÓ4ýmyyy¹“—[üÆMËÒ¬ò=mïõ‹×y9KO§ÓiÓ4Íëׯ__]]]eµçR©T ƒÁÀ ffff91/(À¤–Ïçóôu0³ÏÌÌÌÀ5žIpš}ÒU¤*Š¢D"‘ˆ]AЦrÞ€Îj²ªªª3333¬Y]„¦išŸÛˆ*•JåæÍ›7ÙÁ”†ž³ß ³®êeöF£####–eY¹\.ÇþžL&“Åb±H[ŽŽŽŽb±Xlfff†5Ý»¬8š¦it…B¡G=RUU•i~ãÆ J¥RþóŸÿÌö‰V[¦§§§ 9YN¡ûpwww·a{ÿ»ï¾ûîDz}·D&{o&“Éhš¦|)‹E8ižnóËÃi‰ LÞr ­Äš¦iöõõõñdßYA¶Ž“ÉdRT_¦iš×®]»FHóÊ·“Þ«išÆ›Ñ³ã†l™9ñZ<ÿÛ¿ýÛ¿%‰Äððð0aýÇ>zôèQ»ÖÁ”ìföézppk²æÍÙÊN$ ˲,UUU?Ë3ŸÏç‹ÅbñÒ¥K—싟þô§?ýú믿¦µq°RØi¡^Ê–Dë¶  ðž!.ÂifåAÈ—™ŽŽŽŽEQÊårY暌€7ûjµZmsss³•VB~l]«ÕjµÉÉÉIVAaý:MÓ4y™{C¡Ph|||ܲ,k}}}Á“Ž{÷îÝã)°²ù•1O³ÌÍÍÍÑJH½þÒ,–2Ó4ÍÑÑÑQ6½¹¹¹¹F”ÛÓ†›ö@sëÖ­[±X,fY–õþûï¿娬ò=mïå!*3'^9¹Ñ™nב½`J–™}òœzœ…B¡Ï>ûì3§Â«T*•žžžÐûúúúLÓ4Óétš•J¥2<<<̾£T*•¦¦¦¦,˲úûûû‡†††dËÀ Ðöìßzë­·––––h¥Êùùùy‘òà¦ü ;ÿÑh4 fAÚ ù‘5ÂLŸG2™L²Úµa†¦išeY–Ó»›…Œ@4ûEQül?"VVVVù±ÈÏÖ7,ÁñÞíæ^Öb»Š¢(ÐÖÜæ7Ç¿þú믃Á`0‘H$@`eee%N§Ù%YüXn{•pSÇ€®ë:ø‰----ÑãR³Ê·“ÞËZ¯;+¹]™ñ¶½ÓÖ„Žö»Ù§ÈŒÆ²,«Z­VÙëìà?555%ãOÈÉ@?:::jš¦éäˆìííí•Ëå²ß&h˜qƒ¤ò)‹Ev©âòåË— Ã0D¹Lù³89ÿ]¹r努ªª—¾lyÚ1777Gw ááááÿýßÿýßp8µ‡VàdØßßßw²r´j #ŸÏçÁrE_—uÈÜä—¾,„ˆ×Kéþï&¿ Ø^½zõ*ôùµµµµp8fßKK´I—íW22é,#jÑh4:???O_^Í*ßÓö^BäËLÄÑ6¹v 3û„™ Ó×ññññ³gϞѿу¿išæë¯¿þºìà€FÛŽ5e€h†ò±k„¢ü»ýÓψ´^/ýf•'(J¼öÐ*@ YÖ×××ÿßÿûÿO¤]¹råÊÎÎÎN«LɬϽ”uåÊ•+ÍNnnnLþËËËË…B¡À[p“_X‚2MÓÜÞÞÞæ¥+²0Aû}øðáC‘ŒtRàC/Æa\¾|ù²èÞf•ïi{¯L™±>x¬5¡é¯ÛegŸÕjµjY–Å›5‚Àg·•у¿aÆùóçÏ{Qx@Á½fÇ~Î@¯\¹r% é4P>v—/³'ç?Bì½ô› ½4!k’ƒ-‡~æ¼ù ùa|  T*•þçþçxÛÐÀQ•géi°-ÖÕ!1X":::: ‡Ãa¥Ý.¿¬\€þ,Óæy \kV\6ÿ O:õ^7ÐkØ"YÖ¬ò=mïdỂŽ]}ºª¢( ½.ÇïÝ»wz¯Óƒ6›Í:ù7^À^§×²é™„(’X<¯®®®Šbx!ÇggggíÞ3NMÓ46_÷ïß¿‹Åb¼±—Ù?˜÷í‚¶ˆ¼ôéüˆff¡P(‰D"N T&“ɰ;è¥Z9¡#î±iÙù24ÂÚÚÚša†È Ëår?ÿùÏÎ ÑÑÑÑÃÃÃÃf9Ã҈ʅÞ-ÓjSw2™L‚着êèè託üŠÖ¢aÂà¦ÍÓ@½Š¶²íRè3ô$‡ö0ï´{ÝrŒBè%–f•ïi{/!òeÖtš ˜‚Õ»øÌì;db.óB¿ÚA‹Óý¼ *nCÓiÉl+t:Ï€­ ·åOˆ»°˜Ne$zÒpúf»:¶ dÂÃÍ9¢gxðÂ$³eÄ •Ìàšq€›riE@^ût[Pþ¼ÓÉÕ)°l_ó£Þ>ÿüóÏÙ2ë„{ÝÔ±L9ÐuÒŒòí„÷º ì¶ÌìèH €ÛÙg¥R©œ?þ<»×Wf†ï½íˆ<6éõXž:¿ü+t]×5MÓìÖ"R©T:wîÜ9¶|À‘‰]Oö2û§·¼ˆÖUQ™ŠòÃây¶jñÞÍ:ÉÀÎö~xÆ‹oˆ n­th`¿óÂî\ Ã0Î;w®]³Ž‰‰‰ vÖé6¿ù|>?333K‘KKKKÉd2ÙHÞÀ—¯1.h‡c¸V,‹¿øÅ/~щ÷6“f”ïi|oÇà· ‘ÓÓN#^, º™¡ËÒÉåßÝê3šôÞ÷Ð3~Ñì¿Õˆ–Ú‰Ý ªÕ‡Í@ûkÆÁDˆwÚÑ._:γO™™íYÖÚ½®UÚÑiåO›!J[;àü¬<:ôïÌÌÌL8;YVšM.—ËÙíPh%n"p6m¶†èŸíÜE‚ü˜\.—k·‚Œ ®qk]«|U zþøã?n†Õ£S¡ŸN8ZÚÞY´¬oA'ZÆÎ:g±]"¯n'ÆWÉDI›€ÏÒà躮·sÐñ.Aš «˜EÙ€ È+N4Bì AAAö!Úï‰ g:ê%š$ét:n‚œF ò$œpX­V«Ÿ}öÙg¨ ‚ gpΡgÀàÈ–"^¸äL&“áÍš …BœíöÍËàÅ­ØüÇãñ¸išæ«âŒÙ h‡C»:‚zD‡D9E°áÒ7xqØA–n¡ ½¢cLþXZZZÊf³Ùt:Öu]¿páÂ…jµZ¥U*•ÊÍ›7o¦Óéô_þò—¿„Ãáðµk×®Áïp„ëÆÆÆF½~†•“€tš©T*U.—Ëkkkk““““ýýýý^N½‚#“³Ùlög?ûÙÏïÞ½{·±ù!ëëëëýýýý²7•J¥LÓ4áP$¶}³õÊFȤ-2ééw¼S18=¬¤ü¢V«Õ.^¼x‘BØ:‚zôrîy£ÀáP~žä)‹[Ë•ÓÎÛZü*mF Y 4JÞ{y³Ñ ÈÛßÉ @( ­­­­ÑùãÍÖx3Þl‡Ž”¾—7Û·›Ý8ÏÒ”’FßÃ\l‹f캮ë´EÁMž ‘[’ð '@QÐ’FúÝ¢Ó+íÂGÃï­²89I²ßÒÌð×¼tÙ6)›ïfÀö#™Ø!¨ žmènNï"ľQòÌÈ¢¼Ð‚²UB«d2™ o°g˲P(xßO?Ëšú ‘WÖÒLÀî9'Óy§)í‚—¿F—ΤYüh ŸÏç@ ÑSôÜÏçó²éÍÍÍͱ'ðu:¼åt:ƒÁÞÞÞ^B¹pážyq¿¿¿¿hhhˆB yy"^( E"‘!„Цx8Û|kkk NAƒSÔ:%Î?â?++++Š¢(p6_Ãò Âgzzzš“å(èK]]]]áp8LÈIluž’Édò´É<ätбÛiŸÍÍÍÍF”ÞÌÎtDZ3ÏÒÁÎØe zvÇã««««KKKK Þ‘Á~2 ½®>888h𦹏¸¸èG€hYªŽZ´‰¿S­(ÕjµjY–5>>>3XÀÊår|¼Çãpüë‡~øái1ŠfôÑh4:222b†á÷±Ëí¶" WN€3W2™LfcccCQÅ0 ãöíÛ·½¦«ëº«ªªnooo7*ÄççççÙP­©T*e7111q|||̺ly=™L&{zzzpöï]×õåååeøß¯öã7Ïž={v|||‡Ã]]]]„¼´±Îšn`û‘¢(ÊÆÆÆF»=âý¤R©TzzzzXk$\V;‚&z¤y- Ь 9èìîÇãñÙÙÙYB)‹ÅF—"À¤W,‹ô ¼»»»Û0 Ãë{ 9Þ{ï½÷ ø ¼wwwwîýÊ•+WàZ&“Éhš¦Ñï‚£aççççi¿MÓ´F- ¯2´pö£NE@;¢á)l^a—¶’Éd’UàdxþüùóÃÃÃCvy‰ïæëL&“Y^^^.‹EÈßíÛ·o†a,///ãv1oð¬í°ð& ÈÙàoØ ,% …>ûì³Ïb±X ®e³Ù¬Ó:lgËf³Y?µmVøÕjµlÙòJ8ÏÌÌÌÐßT«Õj|ðÁð>ŸÏ_¸páBú„b†ñÉ'Ÿ|òÞ{ï½G?7<<<\( p}?‚ØQ«ÕjÕjµ‹ÅbàâÆúÆR( š¦il¤Ûk:NÒ¨Nò~ZÒTEúD¿ø:3»vÜ*h‡6¿ö³Ã^ùt:¦×ˆýXâ0MÓÜÞÞÞvºfwÀððððo~ó›ßð´n0ñ8øwtÌ@¶þ y¹´fôåååe;OmÑóv~,~–ª™™™‘2žL&“3333š¦i¯ÒrÀiÆIvâŒ^#ä¥Ù ÌÒW¯^½Úhã ×¦gfff!dvvv¶ÕëXù|>ŸH$–eYôõX,ûæ›o¾iD€‚'¾Ý=v1x[ôx÷uê–©³D>ŸÏC;&ädð•õ§€Ý5,íXS–ennn®X,íÌü…B¡À. rðüwD>=^hVP0äÕá5B^F³,ËJ$ ¿;r>ŸÏ‹Å"»=‰…nü~æ¡T*•‚Á`„.(ìÚ|3 þ@  µp6¬Å}Ú]—­v¦äµ#^ÀŠFo)mdëZ2™LBäG^à*X8-æÿ³´TÈ'^ …B¡ñññqBü™ù‹hÇþY6•¯²ÿ õ÷÷÷·2âØòGo)…™ W_€¹¹¹9ÖÌ“»å¤3Á]`´üV­íAz~5NQþ£ÑhôÒ¥K—y©˜€¤c©Cü{v ᫆߇!í£···7 éå©ííímÓ4ÍH$ñº”Ä.¥5ËZè7¼(™v×ýx»½ûñ0D–×jµZ þÀ1 8:::¢½øé +ôoôsÍntôZùÁÁÁú‚ƒƒƒQö»à9:2IØß ßx|||ܬ­eâ7‘H$¢(ŠBo­T*•:¢¤J¥RéOúÓŸ9 „Ndrºy±£ÜiÑòE€OA±X,²¿†aœ;wî±J¥R5MÓ„{ŠÅb"Ÿ½ªðB ·;ô)âéééi^È_^ˆàWQ˜lÑu¿Þ ƒÝi€Hûx• ò{ é´Ñi§B}ÐùiF¤QÑi‘¼¶mwÝkº­no®á£ÒrxÛÜpªý€@¦…ôi]C„o9mŠ‹_8 vÞ‰“ì9~—]<›¦iBžxÛbõ1Êd2»ÓDY…HtÝ-~½Ç-¨ ~ѲÀJ¥RijjjŠ]f@ÚËi>4ˆ=bõïìì융ó „6}ú# „›¦ÏªhÍŽb'ÚÝÀkÛv×Ýâ×{A—ðNŸïéÓ§O÷^‘N§Ófiææææöööö^¾|ù2 …"‘HdwwwW5=AÔè³{{{{OŸ>}êóù|.“SN4ºAnîÞ½{7‡u]×;;;;S©TªÑe"·‰Åb±¡¡¡¡jµZ ‡Ãá[·nÝjt™œB A¶‰Çãñh4Õu]ïïïï/‹ÅF—‰ jE>ŸÏŸ?þ|µZ­&“Éd<7ºLA áêÕ«W]‚hï¿ÿþû.CÓ>ð—„B¡ÐË—/_îíííÍÍÍÍÕ:_ìc¬U^"ð{šå{TüJ‡Üovwww#‘H¾Ãí„ûÖÐÐÐ|žN§ÓØnÔžét:½Ç¡âû¥«‡_’÷óËò¾|üNNÓ» ®CÕ:;Jï/ÊG„™ì2êÃ0–¬¬í´‹è]duæÆ¼ÀçÅË Õv7"ÇEùA}ÉÞWÔ>u³X` Ä•W«‚àFvÚØvÁn–7®“Z*Enƒë¹QA[¢IÙNß⩨ߨ(OŸ>}úñÇ í)ì²¼TëÒHj¥ðÂOTÏFBÖiz·pk¢9¬ïÏØÁIE†,³>Œ1’i²ñ‹1Ëv‚GùT•4€•IUö~vò•µ×Ó§OŸ~ôÑG©”¨‹¼MöP¨Zul¨pü|ȳ޾èdf¢@¤ÜXUÜxß±a¹½‡Ýw’ .Q?…ßB‚ºxúôéÓ—/_¾ÜØØØÐu]ßÝÝÝÕ‹™àuC¨e›EBóýBô.NÓ»]Àò[I_K™¥ÒdrÚlìfcЩ²ÓLÆŠ€qh%­Lñ‘½'Ô1´3No¥o‹¬5*Ö¾îøò0Và ÀáááaMÓ4]×õåååeÆëîîîfŒ±r¹\ÞÙÙÙ©UÞȳެ¯¯¯«ü. 5MÓŒÒ@g ƒÁÖÖÖVÏ+Z[[[ƒÁ`P¥»ñ  ¿¿¿?àï®®®®žžž•´n0777F£Œ1–Éd2D&“É0ÆX4ª Ћ/^„ÿC$»Çãñ<|øð¡j™J¥RéæÍ›7ý~¿ÿïþîïþnwwwWô»Ó§OŸååñx<‰D"¡šcŒ …nË\.—ƒïj¥<999Y­V«ÇÈÈÈ|7666ýB×u}jjjÊíô¦^ï_( VúŸ|>Ÿopppòyï½÷ÞÓu]gŒ±jµZòx<ž¶¶¶6>¨3‰Dào<þpÿ«V«ÕÉÉÉI£r‹Åb[[[?v¡\­­­­Ùl6+KÿÁ|ÐÞÞÞîõz½ù|>o¥²ÙlÖãñxÚÛÛÛ[[[[UÒ´´´´øý~?cûs´c0 Š”H˜{+¯X\\\T-#( ­­­­PG@ pÿþýû2å5•J¥.^¼x+ Éd2Éc~¿ßßÒÒÒr ^3aæ¯VÕ Z¬RDš-¯-ÙÕÄì–ƒ·€àÏðŠP4añ+Nñxûì3#‹¡j]‰Ã9Pd†¶c®½¿e¡^“>câF«G¾Œ›Ùp#Ȭ/PvÞR >ƒú³”ìÐ.²6à/ûÞê p ¨+œ‘Òifš3¢VÊŒ™%G†Ù*êD&¤ÝVdÂ\u|ÛI/R–Ìù{{âÉXÔVÛÛNù­*0|ÿå][üóDîÑ{ñ2GÖ·øñ,ê[¼Løú믿–Õ…‘‹€Ç¢zäß§ÇÏ{'iùöÕ›ÕŃhŽ4ÊÓ ÜÎV\'Û`‚€ÏAàü[­V«¥R©${9ªÚØØØX]]]õù^”J¥R¥R©¨V” ¶···Ãáp8‘H$ìЏIGGG‡×ëõn¼Âëõz;:::à{>0Ndˆ‡ës}}}¤ðÏ€ö“µA¥R©”J¥’,¨¥§§§§«««‹±×m»¼¼¼ D}}}}µZAû|>_0 Â{2öú:ÆK$ j, …Z”Á)ñx<Ö–B¡P€z ;wîÜaÌZLŽ*•J¥····³³³òeL^oÜ% ÂÊçóù¥¥¥%þóÉÉÉI•ñm'=È$è{Œ16???² ‚­¶¶¶¶dA˜X1¦læVù!ðLô@{x<–Y¸ÿCÙl6‹獵§§óù|›áWÆöɾ¾¾>Ƭcã 8ŽfŒ±k×®]{üøñcYz<§är¹>2úÊ•+WÌÆ-~>ýíÛ·oCzMÓ´áááa·Ò2v0X{eee¥X,wvvvÊår™1Æxy.&þ/¿üòË¡¡¡!£y éáoÜ7øñŠƒ‹+•JåÂ… ð8/ =˜Œ1†ÌéÓ§OãÎÆØë† @ÜÁ`@â‰ãÞ½{÷Ì*É ssss0ñçr¹œ‘a¶Ò’Á kÜ9‚Á`ðÌ™3g4MÓÖ_¡išvæÌ™3¸žU˜™™™ÙÛÛÛ›™™™Éd2™X,³’Þ ¸CBÛ‹ÅâÊÊÊ cûY?ŸÎ­:ÖÖÖÖ{ÝéqÄt¥R©˜E7‚P(š˜˜˜`l?BüÂ… nÞ¼y¾íZ¨‡²ìÑΘõÈu+éy™$"™L&÷ööö6777!šO:‘H$òâÅ‹0a9ÅéûOMMMñBÚjä?ÈÓjµZŸŸŸ7û=,D{=†UóÂ21Æþöoÿöoñn#9Ë·¿°1‹·š·»“´V =,˜“+€•‰ÊŒU¾~qÞ¢2ó;,z{{{ÿùŸÿùŸùÝ';ذÝÝÝÝ éÁV‡¾¾¾¾wß}÷]¨D^0a È………˜8`siĽ{÷îV_ÏÉ‘‡o ?ÿó?ÿs(t ¿üË¿üKøž¯Sh¾a‰DVqüYÔÝÝÝÝø9ü3`â”­ða ˆ&Ü!yaƒ•:~Åá¢Õ¼_^‘E “J¥RF«^«J£ xë$¬ ²Ùl¯'&&&Âáp˜OÆ^hÑV)Çã¾.ª§Z±ºººº±±±[Pœ¦·B<c9äVËÏ›¡±¬dl[àööö6|¯â¶„ñf6X‰)Lx ™ü|ûí·ßfìàŠ¯DYd³ô2g'-¯@@½ííííÄcƒƒƒƒ2·?ñófz3‚¨ÌX‘áËlä}À'«`0<þüù@ xòäÉ“¥¥¥%¯×ëíéééÁ¢ºÇ½d³Ùì7n¼xñâ¼0V6dQ´µÄï÷ûûúúúÀuyòäÉ“`‘i¥"³z±X,Âêq|||ÜçÛßFÔ××ׇŸÃ?ãáÇ …Bï?ÆA€`©µ¯ÈüßÕà €•T2PN¾ócsüdéT(#./6?À£G0¶c-Ã+<3WÝqÌå¹\.'2½7;"³>v1ÆØèèè(õ]ºtécoö!'ç¢ð‹Æö­Yׯ_¿.z¾‘KUQa–ÞIÞ|Z,ûŒ8°¿ží+Ÿ~úé§üŠßçóù=zô[¢@`yyyY&;¼¯ÿS¿ßïÿÙÏ~ö3Æ^¯V5MÓ~ö³Ÿý K-¥S"‘HäÁƒðL&“ét:môüÍ›NTéíííåê4Âápxwwwwkkk V¨~¿ßo4QÁA"ccccüw0™Ã!<ðþðü ðçr¹Xb°ö?>>>¾»»»+:ÀDdþÇufäp³N1бBãóù|ãããã²4˜FM–øà¤J¥R¹qãÆ Þba÷ ˜ñññqh'#WÝaC4ùð€ËƒW±±WÔ¾´bø# ”Çs0€±×.ƒh47 ¤Ëår9MÓ´………Ó|ÂîY»àÅAµZ­^¾|ù2˜“Líf&kÞäÍc5½“¼ùï‹—Á˾J¥R9wîÜ9ÞÔ?222‚¯Ï†~ Ç–9‹Ådrõ c¯…©×ëõž;wî¿Zýàƒ>Ð4M Jð¸CÃ`ÄAG¸ƒ×êÎph,È òüÅ/~ñ üÜí,2ݸ¯(AÐ o:ý–±×+ýd2™\[[[óx^›¦a2oooo?{öìÙÑÑÑQ<EÏwß9R©T*N§DÏ02ÿµvàí…ƒ @¡ø«ÏåÌír¸}5MÓFå®ZVÒª”IÅJÀcu€“÷%ˆ#”¢ …BÏŸ?n´‚6R ‚ ‚hR¬ž‘íóù|ÿðÿðÞJ AAM†êÅ.F®ŒZA¸}\,A`d ­þ ‚  )D­‘5º\AAAAAÇ„zŸŽE47­Ûý°ŸúFs€Û„?ÝÌÊÉh€s1‹ÜÀ»OÈE¼ÀotyˆÆB @scvP”ÙKüñáüabªå8Lrƒú0!ÃΑÜGþŒäF—Ç.nß Úó~‡(°Ó©`'áyF÷ hcT"k€,­èt9Æ^oíT~‡QnàzR99Ž8úÈ”éc£\½zõj£Ëà0iÉÑ$f´‚i´·ƒÑ®ŽÃdútëÈf«†Ø)ßaéÀ»¯}?444¤R߇]n¼ÿþûï7º Ä>üN¡z*güåt CU’vJ'd_8A$üÍ^"‰D2™LFõ÷Í ¾s{zzzÚì’à"þþp;¸ib2ºí±ý‹£:;;;á’žD"‘`Œ1¿ßïoiiiQÍGtØO=&¹P(š˜˜˜€¿@àþýû÷­NÞñx<žL&“Œí_'ÚÛÛÛëÖÅCnö ¾=­ŒA;môÇWÈÒ.,,,ðWS‹òžžž®ußÉ4‘Õ0Ròøßþþ÷¿ÿ½j™Œ+iy9ae"´º¥וŠÂ-û þ\Öwy…™¯|>Ÿoooo‡±4111qX¬J®`÷‚£ôfâF¾Í‚Šùߪ]i·.•€÷ŒÇãq#‹?à¬L*fþåZZøº¶[÷õrY¸Ñ7ìŒA'mÔ¨´vMj² Ódù¦ºj”àôú]£ ¾¬.~ìÆ£ØÅ‰ À±«Ôi0cæç³Ë„ÿQò}Ô²Ó8òFÊ™í ÏUu_¨æ%RVêÕGD~h¨U%ÆIY§yÙƒNÚ¨Qií`Ï¢ër˜êªQ €Ên£IÞÈ„oÇyÕk.jX J'1,|Áù N§Ói™àÄF×uÐÊ$Š·Q–ƒZ |'Ï™ÇTLfFϲê‡Á©’—ѵÍõ¸ÈÇÈ/ëóù|'Ož\&+[·ŒÚÙI™3Ff@§Øi#7ÒÚÁi=vÆ “6jTZ» ÀÆÆÆäùÁg{{ƾéÃPW¼ðõ×_Í[9TêMµ£IÞH9À «ŠF4¦di°% #ê“N,ðNÒž`Œ±îîînø`eeeGÞ®®®®nlllÀß/^¼ˆQÎŒ1F£ª•îóù|ƒƒƒƒŒ1V­V«¥R©´¶¶¶ßã²5 ðÎü€÷àëÿæþýû÷@@öl»‘æ2òù|Þëõz=G}^,‹mmmmÇÓÖÖÖfyÝÒÒÒâ÷ûýfùf³Ù,Dýc …BáÔ©S§Œòðù|¾`0„¿qÖ×××áÿÁ`0èV}9i#»iÍâoxÜŽ/±3´Q£Òº ÈÄwß}÷Ý`0¬V«U]×uÑoB]}òÉ'Ÿh𦉾s;RÏ9š¦iøNŸ>}þ_*•J²Ý4>|(Ú“ËårÙl6+Jã¶|öûý~Q Øq[ŽŽŽŽÚuwÊÒžà; îŒ1V©T*¥R©${ðøøø¸¬c…B¡ððáÇ¢ïzzzzººººclwwwwkkkkyyyS____³líraaaAÓ4 — ¿Ç½{÷î‰ÒŒŒŒ„Ãá0cû‚vhhh¶È U«Õ*cŒ…ÃáðÈÈȤƒ­^"ð„- \s›ŽŽŽ¯×ëUù­¨Ü¼b·Ì"aƘóÉÔn9Mk7úF­Æ ¬•V´R›™™™ïgfffðwF”?üáÐu]?yòäÉ®®®® ^vºQæfJ›Éd2з2™L>þþþ~»eàáç<éceÔ¨¾+•JerrrV­V«üg'ã—ßòœËårðÔ,ˆx¥Èår9~ wvvvÂ8Ô4M•ÝnZ¥sdíóù|Éd2‰3bŒ±D"‘0ú˜`0VÎ;;;;år¹Ìc^¯×ÛÑÑÑÓˆÌ;F›‘¬F¨îí½6Ÿ`¡ÈØÁŽï¡ëº¾¼¼¼,zWl=áÏÈçóùéééiøwz|Tÿààà`kkkk,‹™¥µ n/;O¥ÌªV†Zà¤Ün_'XévÆ “6jTZ7ùÓŸþô§r¹\†U^©T*íìììˆ~{ê*—ËåR©T þ¾}ûöm¼Âæ-ÃNÁ‹(˜ôñbµZ­VççççžÁ[––––ŒÎe±;~‹Åb±···[5qùAÉÂó?þc¯àËT,‹³³³³|]¸•ÖÑA@•J¥òüùóçNž+&¬i=õLåXᆄ÷02ÿc­/™L&yE»QDÅ õK—.]ÂZ§›̸…2ãɧž8i#'i+•J¥···W¶’çÕ™“¾ag :i£F¥u“ííím¼J5R†B]‰,Ë‹‹‹n<[D©T*ÁŠ,P"K•Ñ3ðŠž±}×´‘+Ú‰|æ-¢éæææ¦‘{â|ÝN{ÂÌÄ/zYL$‰<{öì~1¼72íUz4Â烃ƒƒÍ´£X,WVVVÛ/Û»ï¾û.œþ'3ÿ[ûãÀâÁ u3}#©u™e+h•É”·XÙʼnÙ-ß´Óz®åtbå¨EZëà8ÞriV‡xRtb†7*s³¤5{?·c-pX D–*YúPèà)žÀøøø¸åÄï …B?þøãXÙ°X¥U&z¾ž¤eì•wd‘fƒ?ÁŘù\°Fgt¬Èïi4½^¯Ì9"!`6}ñõÐÕÕÕ5:::@eö>V÷• $ì©Õ–8@U`8)³‘ÀýÔ(è0à$nAVÏ"7™¨o؃NÚ¨QiÝ&•J¥@FÈË;ue6îÝ®gÞ ƒ¸ fîDZ±±1X”–Ëå2ößߺuë–[åäó²Ã;wîØMï$-c¯€ùùùyÞÜ?À‚÷oC €Çs0(¤P(Z[[[ñÄËØ›ç ›2æ~‰€?GÓ4í“O>ù„±ýÈàÕÕÕUÑïùÁˆjD¨šó#‘HäË/¿üÒ½7SÇîªG¥Ìx€ó«ÏPèõ} üoà¤jÕ¾vˆÇãql‚’Éd’WœŒA'mÔ¨´â°×•ha;Gj.ÏéÓ§Oã2ÉžH$…¿ÿæoþæo®]»v þ¾téÒ%>¸ÓÉø…r麮ƒÎhAêñ¼^Tò lQ0ž[1NÒ'ÛpXZZZbl Áð vvv¯2p V"ÂápøîÝ»wqFét:Í %Ü ¢ Ç‘”µš² v‹‹‹‹FB[OdÛ2"‘HD×u]õ¨Ù/¾øâ Æöµ\koÐTËÌ÷'¬µc­W%ˆ[ø‰ØI¹Ý¾Nà…Œ%^;ƒNÚ¨QiÅa¯«h4ÅÊã­[·na“·[®OYÙõ«_ýj```€±7¢_7Æ “6ªwZ«;€ð6ÀãVWVÜRnì%BÖ^ª"äæø•àûJûÈÞÝIZÃJæÇ(V ®8•#hUŽ}5n½•SöxT„Ê‘N¿¾[×{­•#beX-³Yrû@ÀI¹Õ¾n!›üsg :i£z§m´Ьu%j{>M=O”¥3KÏ—ÙÎ)‘vÆ/Z.~¾L9Àuf”çË—/_~ÿý÷ßÃßfÇ9[Ikø; €Q…iM*GÔò•Ö àçäøc7ê×ê3ìÈê‘¶NÊ,ª«Z+:²|Uë×IZ·€ñ+k+7Ç “6ªWÚF+ÍXW{{bÙ%²óu`E‘µ«ˆÊ¬ÒUZÕgaTÇ þÿ,•úÆÏ°£Xª¦%!Í XÍ£‘e>n8QL‰ã?ë¡PQêåp3Ÿzº-ŽF~AR´@ÅDmHmøIŽÕu]Ÿšššjtù‚°O>ŸÏ_»víZ.—Ë5ó¡bA4>ÈñóÏ?ÿ|o¯ùb1‚ ‚p«[ ‚ ‚ ‚ ‚ ‚ ˆ# ¿‘"Ž‰Ã Þއ0Ûµ Š®†±¡2&øó7(B› j‡ÊB„ ²Ó¦H #v³S×ÌöåãEÒétÚî¹ A¨a÷p3 *8÷LjÆ]&p˜è­ìµÆÊÞi˜t¨A¸‹Ùi™féHqx>; €S“éa„_)Úîü”µ0_áƒeŽj{4ï¾BöýÐÐÐP3 'î’FeÆŠõfìãNêØ Y£"g£œ¸zõêU;éH`çœj§&S•²4cg´rƵ \wµî„Xà—•(\ªkV£Òò^ç/ IDAT‚Zµ­œ(áõTDgÔÛyŽèžU™e”ÖÊs¬à¤ŽÊ+rÆ®œÍµif—©Îª €ÙMž›\= pmmmÍj8]î/‚; u]ׯ_¿~ÝÍ26‘H$2:::ªëºÞÙÙÙ™H$Œ16111a¥ÃÃܺ®ëýýýýµ<É+ŸÏçÏŸ?¾Z­Vù»Á*ÃÃÃÃp¯8cŒiš¦ƒÁ`³¦õù|¾û÷ï߇;ÞÛ¿ÇÝLû|>ß_|ñä››››ÛÛÛÛáp8\*•J•J¥¢RöÃB6›Í¸Ã$‰D*•JÉÒÍÍÍÍÍÌÌÌÔ¶tîᆬ±"gìȉ¹¹¹9èkêov4‡Ãáíííí#q¸šè òF­›Õ+4Ù]Õªeµë»r høG=¶Cf™RéÏH+»[ƤQz'q õÆ- €TWþ@£•d7d]9cENÈî®hf €*f€Z¾« ®Y|>ŸV)ÕjµZ.—ËŒ1Ö××××L¤Ñüä'?ùÉ©S§Ny½^o>ŸÏÃçÙl6ëñx<¿øÅ/~¡òœŸþô§?õx<žzŸáÏçó^¯×ÛÞÞÞÞÚÚÚZ¯|ë X¦c¬\.—Á* ƒfB¡iC¡PhtttÔãñxð 6‹Å<çÎ;wdã°X,ÛÚÚÚb±XŒ±×m܈þÕÌ\¼xñ"ü¿P(Z[[[±>ó¼ÂÈ’ #ý 7Ê놬±+g¬Ê‰jµZzôù£ß?2™L¾óz½ÞŽŽŽŽz—é€ ÓpU´¸žžžž®®®.Æ[ZZZzòäÉÆö͉ýýýýFiäËûK­jmv­¼¯483 òùóçÏGFFFdïûÖ[o½edn…ºšžžžV©+\ÎÏ?ÿüsÞ—‡ë^Õçµ····½½½ýÝwß}§j Õ³LЉVyvî"—õ -¿¿¿¿LéOž>þøãeÏK‰žqÿþýû|¹C¡PhbbbB”FÓ4íìÙ³g߯£zv’öí·ß~ÿí÷ûýx‚dÌ~[‰òU­ç;wîÜ1zö'Ÿ|ò _N@Ìä¤o@¹{-ìq€+îëÍÖI=7*­[XínŒßÝÝÝÝ­­­-+å …B¡o¾ùæYY£øL¼ÉØØØŒ]×õååååz—ထø˜ÎÎÎNl¦­B°y ^dkkkkwww—±ý‰¦¥¥¥EV;ùò‘Ò™L&is¹\Îè¥GFFF`% ¾'H‹}Páp8<222‚ÓâF³š¯“÷u#-ø °ï)“ÉdZ[[[ …BÁí2ãzæë —a```ÀLá‘¥¹˜œô ƺ³@Ø////ûŵ4"­“znTZL¥R©ôöööBZˆEPÅJßpcüÊ€ˆnlÖ‡ï°kÇ(_‘/8•J¥à·¢ 2K:»ðnœ½=±û'‰D¾ýöÛoæçôýøþ‘L&“ðÝìììl#bmþ_ˆ½‚ÿA±X,ÎÎÎÎÂߢU6•ËåòÎÎÎN±X,®¬¬¬0f`7_ü7o2»råÊ£ óLOOOcßS>ŸÏOOOOÃß¼ûç[( ·oß¾­š/cÎêÙIZƃí\xU¹¶¶¶V©T*`Ò)kvóÅõÌ·ÑíÛ·oC]™™·Òòùðå°Ú7Û¸beee¥X,wvvv °Õ(`§iÔs#ÒZ¦7òé;évƯ]du•N§ÓØ% â&"ö'ÿåååeÞúîÜxW`3‹²F¹|XdAXSW\xÏ0ø/{s :Éï8àóalE±¸¸¸¨’6™L&òŃO»¸¸¸ˆƒhŒòuò¾n¥uB­ÛÈÈ4î¤}­¤p_…ôXI2ÒõN뤞•ÖMœô »ã×*²ºJ§Óéd2™Ä»¦«]]͇ÈÊ\ON0öÚ”¢2‰ðÛ‘°ß’±ý:Ñä ÚÆä$_ŒˆTq3_'ïëV]Y¥^mÔ¨´>>Þ¨sjN0ÆØ;wîò÷-!ÚÆä$_ŒÑ.ƒZb5_'ïëV]5*_³º2:m®–iy°Þ‘«¤Qi1ÍZÏ8-ìCWÅh‹X=ûàtÏöÌÌÌ Lþ‰D"!S9Åb±ØÛÛÛËûÍáL Üz{{{›é$Ëb±X¼|ùòe£X³zq‚_yˆ‚½p` Ü"ƒ7_:É›E{ÓdÆ[%ŒÒâ`¸óðiùÕ˜Q¾NÞ×iÙ¥žmÄ›r1NÚ×JZÆÞ ”¡|X´Nê¹Qiyœœà¤oX¿8¨Ùʱ΢|˜ü­Ôá \·"ëîOµ<;ŸÏçáŒÆŒ]änÀËràm€|ç‹Çãq#ó/Ö¢Í&+~^³|ñïù3§ïÞ½{×è\i|FlÛ\$‰èº®óßá|ù}ófùʞØùûº•Ö VóÅõÌ·œ/ÎØþNŒùùùyÙsŒÒòùðå´Ú7p)‡8bÛ(@´žiÔs£Òº…“¾aeüâ fƬ›m''''ñ w™L&+¾ÌµtiwpÝòíÇãØÝVkE ÷M£2ØB‰D"²gâ÷ÃVw~ÇÎdg1‹À§ ñš»è0£[“ìæ Oy³’–/· Qô±hë‰j¾NÞ×nZ\^XYá6­Jümcn”YµžùŸj:Qyô ¾mEÉ׈´N깑ižcô{§}ÃÉøµº{¿‡jŸäóT=K~oïàû–tVÚÓ³ùUóãûŽõb6nq±’~®j?;pàõëׯó€®ëúoûÛߊ¾Ã~K™–÷1ó¾3»ù2öæv=+i+•JåÂ… dyQ,‹7oÞ¼)ú®Z­Vøá‡di¼¯“´Np’¯J=«|òoÿöoÿ&;üæÍ›7y? Ý¾·³Ê星ŸŸ‡²` »QisVÏJË?Çî9Vû†“ñ›Íf³vÎ ðùÞ¶(C×uýÂ… šÉ_}Ô¨T*•7nÜ0»o@ÔwjQ¼ëDå<ÆÛx…è;޵躮‹¶¸ž`lPœ:uêÿƒB¡P8uêÔ©ÿýßÿý_Qfxß²ìt,¼™°›/J¥Rüåªi!XDæÃ. …ööövQðQ6›ÍòùV«ÕêùóçÏÿáø|Æûœ¼¯Óº²‹Ó|¡žE‚3“ÉdT.yüøñã3gΜÁ \o*  ²Ó7°γà#ó7*-ञ•Ö ìô »ã—±ý31¬æWþJ^L=êŠØ'ŸÏçÛÛÛÛE¢Yßqþ>€ñññqþ7xÂ×u]ÿè£>úõ¯ýk™ßg×8Û9¾š°ˆÙu“„9¼iÌí+_›õªhœZ÷ZŒß>úè£Ç?>Ê×gîR/åÚuÀljt: ‡z‘azßÿú¯ÿú¯sçÎköÛîˆãÇO]€Ã x$û¾–‘ÏA8ƒÆ/A fðw qx ñKÈ`ƒT*•Z[[[›™™™á¿+ …?üðCŠä%ˆæ„Æ/AAAAA4-ü‰uøä#ÑI„‡ x?ü.pmYknp_ÄÛÓ`Kèd:‚¨7ü‰†*ýo$9ä§õ©r ï‘ä¨+FǺ½ç™p|Ô,l7#¡I4 NúçQ“µÆI}ÊŽn?òu€1ñyÎ4ù쬰¢ÖàU£ÈB¥rð‘ÌÊEØÃN}‚|9Rr…¿¤ÃíÉü8(Äáú"ùÔ‘èDÃx÷²ï‡†††¬ô³÷ßÿ}wJF0¦^ŸW¯^½Z³Bà#4B³#3­eˆ5S™°sšŸUÀJ~FJ„‘IßH°ËÒ)+f7HÕb"±RNÙÍ‚FýÃÊ løýìÞúfT‡µ('ý‘ïŸÍ2ù›Ý ©RNQ‘kÄvˆµ+?í¦3ÇùˆtU¹¤Z—²4"9&²04ÅA@p6\ZÐìbÄãñx2™LÂ߉D"‘J¥Rn<[´G™1Æ’Éd’Ÿ\AÂeª5vÊyØ ‡ÃáííííZLZpƒa lnnn.,,,hš¦ÕãV27€r‹Úú§¨¯„Ãáð‹/^Ð9ùQ®ÀdZëÉ?‹Å<nÜÊçóy¯×ëõx<+“w$‰|ùå—_Âß¹\.çÖÍQ¡P(´¼¼¼VÒݽ{÷n8óŸ …Bkkk+óY£ÊyT‡ÃáG=r[ ¸}ûöm|‹—›}«^ŒŽŽŽò“9(ø²4²[Ђ°G*•Jy<Oggg§ÑuÝ W~úÓŸþOÌ.ü½Þ…B¡påÊ•+Fi²ÙlÖƒ0ºJôÎ;wð¤šËår8­h"…B¡¾¾¾>‡¯f ;ådìµfV¾þ0üUγ³³³²>”H$²çôöööŠN~ ~‡¯¼ ‡Ãá[·nÝr³œ•J¥ÒÛÛÛ éb±XL¥ŽêQ{óW~3ÆXwww7ü×ikkk+(<}}}}Íàê Ü®‰F–¨FºÕp½àþÏØþ•Ð"™d'*'øý„ØŒ fnTïSú×ý×Åß» hæÃ²’^i뺮_¸pá‚[G†òy&“ÉðÜcýÇ?þñðYGGG‡×ëõÚÉSæï6jg»åTÁJÐ[$‰ŒŽŽŽÂß…B¡pûöíÛVò³J,‹aA6888¨²uʨœvcx:ß·q¿ÇÁ…nŽ ŸÏç—–––Dßù|>_0 Âß““““¢1ãõz½fy™å·¡n·¬ º…0ý Ñï#‘HäñãÇíøùeí{œùýýýýVÒÂ< u ýå°×qÝ-÷îÝ»ÿàÍ…ýýýý°ºtãF.·ò㯠uÛ7‹Ë¡ëº>555%ú]¥R©|ÿý÷ß»•¯UjQÎ@ xñâÅ ðolllüä'?‘ÞSÁ[bªÕjõÆ7êq~ûäää$¬v»ºººzzzzQÎl6›Å–Ü·ù¾zíÚµkp±M½ÇŸŒ¹¹¹¹ííím#×€ffffœLÖn >F'N§“¯à•€H$yðàÁƒ³gÏžUq7¥Óé4nóD"‘hV ’"eL8ûí·ß~«òL\?‰D"¡âZÃåõU¼Hnt_³CÝ€åååeðIˆÌ…/^¼ÿßØØØX]]]mt~ï¼óÎ;ØïŸÉd2nûfOŸ>}þ_.—Ë;;;;*é¶¶¶¶vwwwÝ,‹vËi„ßï÷kš¦U«ÕêÐÐÐÐ|ðÁóçÏŸË~Ïû”ñWkp}kš¦áÕm½Ë‰cÀîóù|ØŸÎÇÔbüE"‘ÈÀÀÀcjJVPÀàõz½µhCÜÜÛ«ï¾u°®mnnnŠbexËV`òÅQÓ4ÍÈ"Šü­:Á5#²ø"2ÅJÊB:# ®Ÿ™™™ÙÊ Üךe÷Ž'x¿#^Uð¾>7üôÅb±8;;; c3*o^–™ ëLPð7öiº~f©T*©¾7ÿ~V€@GÝ'o·œfd2™ŒÊ$À›ÔUå`EÈÃï½7cggg§\.—Í~§ZNÞW ðÁ;¢[ê*•JåÆ7À"1000ðOÿôOÿ„ÝTׯ_¿Îççt<ð®A,ˆU®ÑÅJäúúúºÑo­õ) ~2² 8iÀhµ±°*¯T*•_þò—¿Äã.™L&?~üOþ™L&sòäÉ“2wZ"‘H•É¿Q`wîËŽ[öÃýEó„«2Æ D ¨¨ÿ7$p~~~^dFåÍËËËËË͘_4Ök5!Ú;Íw¨T*•âÍêJ9o Sõßó&uÑ× 8-§ÏçóÝ¿ÿ>x“|>Ÿ¿víÚ5ÆöWŠŸ|òÉ'ðÌMU‹ñ‹•5kkkkðÿZmÅšWnU­ªí [í뺮wvvvhe³Ù,ž@Ξ={OþFõ‡Ãxܕɿ\.—¡ÞdŠcjAØf„B¡ÐÄÄÄÿ96 ®Ã;ÊDA¿Í¼Ë¥! ÀêêêêÆÆÆcÍØü¸²²²â–Ýüt]×qô%˜[Ý(ŸÀj^u%ßâñx܉ߗ7©7ëy§åäƒMͶÇf³Ù,emd©Åø«€Šé“/o2™LÖÒdŠwhðõ„ß™G¥B¡PèÒ¥K—ðg0y¨ZLóù|þüùóçñ*Òlò-+‡•b±XìíííåëMd¡‘EÊ‹”~GÏaP[wyp½5D¨T*•ÉÉÉIø{pppðÝwß}›q°R£ó«V«ÕË—/_ÆæÖp8q«ŒØ ƒv•‹l6›­¥`·œñx<«þ\.—³ZF»¦@¶ Ъ﹥¥¥Åï÷ûkUι¹¹9ðC¿S™DøþkÔŸŽÞ5ˆ'Ö@ ¸ÿþ}³~‹ÅbxåËËË˵ö›ªº춃xŸ?cö,#—.]ºtüÎf¸ˆc ŒÜ6Åb±xóæÍ›üçØehÜÇ——wÿ4«¥’±ž€ƒ‘ººººFGGGÁ|V( >|Ø,ù----å_·;¹iÀfQ¿ßïoiiiaìõêÅì@‡za·œÙl6ûÝwß}7444dUûn&Ó?ÞvY­V«¥R©äV9ùHnÕ Aþp*Æ›˜˜˜0š ÜVvGù|>æÌ™3PŽ@ ³’¯ Ø=Å g‘¢c¥D180y¨ŒýÎ?üðÃP*J@"‘H`åëÎ;wŒ~ß츈Ý1Øåà @l!ùóqz~ ÈõÃØA‹C3Ÿqc[0[ š‹‹ÅâÊÊÊ coú.ùß×;?XЉ¬>ŸÏ÷õ×_mU1À¹V‚Ð œ”ó—¿üå/E‚+"°I½Ö«13ÆÇÇÇAèðQòNÊi7’›W:³ÉÀ­ñà~u+À hþb,œù:¶Ó²S×ð¤%ZEb«cûfÿsçÎÃî#%5,“dñIvå§S¹{ØÀþ|«‹.ܯìÊÓ £Ó¹ðJ?߇,3»‰´oÙ6¢zç'ƒ·ˆVZ½½½½ÛÛÛÛ¢½½x¥†·ÑaÌXóž¥ïV9K¥R ›esð&u•(óZÍÂŒœ(”“¿[ÂÊ6S¬t …Â{ï½÷-³`U7Æ€#3¾úꫯ`Ìø|>ßààà ÿ¼jŸ›››Ã:¬¬ñÁ7ðžð;Ѫ Ü@2áì¤;è‡æÁ` +H~»&öùó1¢ã•1pü-’Ivå§r—ÿNtþ¦žA€FÀ`wØa™ô Q½½Ž?AP% è2K½ó3:1 ïÝÛ;xZšj9ùtŒ©ŸFf¶­ÚÞMt³œÙÍFuêF9Ížucv ,?þùª»6Të…ï7vËÉ÷%+yòiáñ;Ôb¡ºõÎÊ {{b@-Ò9½ P•7,ØÄk¿Y†Qt¥R©,...âÏd{럼ÀNðhë[±X,ö÷÷÷›ÕÿîîîîÖÖÖ–•üÜÄr:9¿ ÑÕ ÞôýöSSSSØM#s¸50NN;4:YÒ*f«ýzad`lß} ‹öÏçóùsçÎS)?ÌÆ[ ìÊO7ä.cŒ=|øð¡hí&8v£ž‡ƒfÞPŠÅbñÔ©S§T Î|ýöÀš™Ððžd³½ÇõÎÏìwÃBV2ŠrO$ ™`!pR^3•¸QÎf8¿À àãkô¤ÂØÁ-jü¤ËOÑh4*;ùÌ­ñÀؾ9TeWÅ•+W®ðc8—ËåÜ:h¬­­­­÷çR©T-·ŸáIVÓ4í›o¾ù/LìÊO7ä.ÈÅZ^ ´°°°P( Gå<„c6iÕãb…zç×T\ÁØñAˆièuÀü!nîýo†ü¢™¡ñ@D]‘ÛÔê&¥zçGÍ ‚ €†Z0n\üÓÌùD3Cã ˆš#ZÔòbzçGÍ ‚ ‚ ‚ ‚ ‚8¶ð§5™mƒS—àwؼhtºüÒÁiSˆDÔè§²êšñÈåZ€OÎ;.ï\O°lÃnØjIÛp‰¦7˜N§Ó"¡ÉcU0;V’ö!µ áÏ>ûì33Eõ(B @í±#G ¢¡à‰Y¤¹Ê&f« xžÈP»·$Ž;üôÇMá$ >Xµ¤DCy÷²ï‡†††k&÷Âц·*¹1©ãUVˆAaU½h„ ¢©©×íDüêK$X÷öä7¥ÙÕ¢í(ð~N”™?Ý:'ÛŽeä:1ZÉÉn]SÝöevØj?•Qö^V¶¥©Þþ%;Ç,Vž+›ü­Üª(ÊKÔFFmj¥ÎܸEL¥Þ¬ÜŒ ^%óù‹n´û>²ÛóTÕµÕeiœÊD³ww£lN­¢þÝH÷;\ßð9_6Ù›ø9n¿–:•4ø7ð ;èâÅ‹•7O( =}úô©QG]ÍiÜŽoicìõ Ä÷Ž3333ü 6Ç····áâžd2™”uZ|W7ŸŠ0ãïTw £÷š™™™QQàö¯jµZ=þüyÑ%4Pß ¢ûê“ÉdR4`òù|þÚµk×4MÓ^¡iš6===íÆ-c0™‰Ú(™L&EícVgnº4ìÖÛQÁ¨®@`sss“ï£vÒ`܉áp8¼½½½Í·Õ²]¿~ýº®ëz ŒÙ)ËÜÜÜœL5ŸÏçgìàÅW¡P(4111Ác««««,g=i˜ ƒµ4Ƀ€…NC¤ÑŽ…Ãáðæææ&/D± <{öìÙ‘‘‘³¼#‘Hd```€±7OX»uëÖ-ÙdÎØþÍmVfÝüeF8ß½{÷®ìûH$µRU"‘HäË/¿üÒé3FGGG&ÆÞšg•l6›Å·—år¹œìêV«Ü¹s玑r‡Ã·nݺc!%ÃNß‘á¤Þ;*uíF7e"¾ØNÙŠÅbqeee…1Æ­–Ëçóù‚Á`ÐJšZ3222}zeeenžÄ R©T:N'bžÀ÷U·¶¶¶â+3™LÆó |ª4<]]]]====ª-UÅÜÇã››››"Ïž={†WÌøZÚh4…´áp8¼¾¾¾FõšÉáááaMÓ4þzUþò•\.—Õ¡h%÷›cðäýýýýð7\É‹ßÙÙÙ ÷Ä3ÆX___ŸLi †Ù»åàI“¿Ç^ôn*W†¶¶¶¶¶···]?Çãx×ùB>FW6±X,¿]åšJ¥R¢wæïÇw݇B¡P___|'ëXðö÷÷÷ãþŒë ç3>>>iìŽS'õfÔdu¨ŠrG£º–õO;ix¬ÊD¸ŠWÔ.š¦iÃÃÃÃNÊWž‡Ãá°ÊÂÓÒÒÒâ÷ûý¢}ú4üæ0[ fÀÔ."sßЪ¤R©/TÐ*“?îP³³³³ø~óŽŽŽ¯×ëel_PNNNN2¶W6üŸ±ƒs6›ÍÊò3ü 2ŸÏç@€dü=ñ2À‚Q­V«år¹lö{+ôôôôtuuuÁß"¥*‹Ådu –ï¾ûî»ííím#¿V¤ …BáÃ?üO Ùl6Ë+E|>¼ÿRÕìíóù|ƒƒƒƒð7^q¨ö¿ßïoiiiaì ÀáÝJSSSSð8]œÔÛQÀ¨®ÛïŸCCCCÕjµê$ S™È[¬œ–-ŸÏç—–––;¨XÊÀc†_€ÍÌÌ̈|æ|\Œ/;À»8ñw"·—è{^­­1vP9ªV«ÕR©T’Õ–çÝÝÝÝð¹Ó{4ø8ìN&“IQ=ØI#¢! @¹\.W«ÕªÓ’lî ­ëºþÞ{ï½WxEë+D“¼[€9 ¯ø¬MòZãòòò2T¯×ëíèèè0ËkgggOÐdÎÎù IDATЩ(•J%#!„WèKKKKOž2°¹OòvÁ‡7Sâ~ Úwd8©·£G²Õo>ŸÏÿçþç:Iƒq[&bœ”͉Ào½õÖ[|l ïâ„Õ;coºMøþëäŠk¬ÄË,x"…ê0ÑàùóçÏ7666¬˜¼ÌÌ]"S¹\.ïìììàÏ*•JåܹsçøÕq:NË‚d¢Ñh”a´úgì 6i$ÄeïÃÃ+;f“)o~Ç«R&.«“Ë™­–q¬¬¬¬tttt`MV¦±BÀŸjyøU6¯ÙË°š ¼Šæ•½ÕÕÕÕ Æöë ,V¼õJÔ>Œ½)ô°¹UµïȰ[oG ^Iž™™™1[IÙIƒ±#yx×e³jpÊÇü±Yàñüüü<¼__¸ÿZ]`ð`ùŠå9¶¦[4±»,¬ Qþô§?ýiqqqщɋ7wá‰PLét: &”\.—óf¡P(twwwƒÅÀ,::FµZ­ÎÏÏÏóßãUc¯Í8¢ÀDÑû`øÉüæÍ›7E“6‡a§P(®\¹r…&L@KKKKNýt¢hv\Ñh4ÊG™ƒ"ë™÷Ó‰ü̼_[vwww·¶¶¶ÌÊk'8”1Ʊ¨T*• .\‹(˜FíƒW=üjŒß­aÖwŒ°So‘"ȶ”+OÔOA>ŸÏOOOOãÏð{‰bì¤ÁØ‘‰|\ÔÌÌÌ |‡eÓ²Á*ÚLžâ ÞEd%€_ìñV[^Æ /Ä_1vPÎçóy¯×ëå'@p±ŠÊ…Óà8&œFÅ-l_6lYñ-¸|vÒˆhX ˜¥Ü6yaUÓ4íûï¿ÿvŸÏŸ;wîœÌÜcÇtB]×uý‡~øA% ÍÀ»[Ýš†5Ù*[ä¾Q.—ËX[iÿ2á"‹fÇ»êqëF>FÁ8”Cvß>¢Uœ •XÂXé‰ä4MÓ¾øâ‹/ø‰ÚNÀ©LdìõŽ ‘L°[6œj4–Ý@6^x°B öÍW÷•U¦À>SMÓ´¶¶¶6³ß«š»àÙmmmm²¨[³U)¤WÝN¤ºúÇf\Æ^›qÚÚÚÚøN/â>ŸÏ÷èÑ£G°òÏår9³mUx›ÇspË^e/Ê)NÅb±ØßßßJ6Óá:Èår9l¹ÀÑìNÁ~O§qVP þãÏX)A¸}¯âp «åwZoFÛEýœ±7·Ö:P×*ØÜŠÇ‘IÜN«2‘7•{¨Æ W|Ü !¦¡ ˜¼ü~¿ßÊ¡3€‘¹KD.—ËpusUBßLëÄ«+^ÛÆþg>ø* …~üñÇNþ"œL²Fð€òcdžVI£؉còæP(ÅÐN>>øO!̃¡Ò>XðÙ ÜÃØ©·£FüµN#©L¬GÙÜ8ÃÅ °ÿôéÓ§a\É‚°‡ú€6F¨š» bDž?  ŒŽýåáWW*Û¿B¡Phyyy”•Éî¢ïø#^ø‹çæææd×>ã2ãU)>ï€?ºG³ËÒhš¦=xðàß©ãñxœ/ ¢ PþÄH»ù|ðŸl 6sòuÀÛ'N§ù~w°0f-~C†Õz;ŠÈ¶Êá#œyk‹4S©TJåÔÁ  bdB¼P(ÚÛÛÛݸŒBvå¯p®€èÕD"‘p÷ovîx.—˹q!‡Q>ಽQär¹œ,Z<‹ÅdgÐg2™Œ(Ÿã *§(Õ|xŽÊ9©T*%Ëb[xE_ZÅ—Éí VìÔÛQÁì®Ñø±“F†Û2ÑͲ1öz ž(HµÞðçc8=ýÏ T.j´›âØf"·Lþ€ª)Œ ˆÃ˜oݺnù°cÕõyœ™ð±ËǨ©¸ÜHÃXX+`R‚ ˆc žøju@ÎCUÁÀJ‰Y X ð³a{ÌòàTøùÏþsŠ.'ÂmœÜ·@–ðù|¾“'Ož¬W~'Ož<©bPýÏçóýÕ_ýÕ_}ïöûÁmô%&Œ½¶|€Ò"Û׌ጕ÷°r‰ÒaC¦4‚²èÔ2„O–«Õ9'¢Ë‡ŽÒÙ**}·–íh¥~yëçÉ“'Oª,2,+¢í/8ƒFîÝîÕ¡¡zî՛ÉööÄ«TQžv¶%áNçæ¶&Ù UvnÆÃiìÜ6×ÌiduÐ {Ìí(¢~­Ò¯Œ¬$ª±ÍÔ_Ÿn¹…pû˜)^²¾*«_Ùm{FýÙn_0ƒ/»ÙX‘Ý(i·¼ªí(s3ŠŽ¶Z¿ðlh/UÄ’ Èf €J£åg6‰›UV½•€z+F׳Z}w·Ïñ6kQ­¤iæÉÜ `ŽÃi{8ÁªÀ B+«"¢ß€±ÚMЀВf&¯D²ú6ôI³«ˆòp"ŸÆ˜ê5Ã*“ºYßUiG+ €•úåO¢„´*s®%¿¤Ê vjÂSUÌ:°(½ÕHn+”á|q©Þ‘Ícv÷³•wªÅêßÊÝñvÒ4ódîTˆD"]×õH$‰ÇãñÃ0(BüJE”ÖŠ°=ì €‰*VbTd8­çx<—s'r—WJñgªÖ ^ÖÙ1ë«´£À 0à™nÔ«aFV:~9«[UÀyˆÌ.f+L(—[«,; ŸNµ~Ò¤_¡Zn£U—xÁ—¾—‹­’O²ªV¦fNƒÁýúÍañƒ›ÅU}õêÕ«FÏ>J @=‘ŰȔK« >xåÞ‰¼•7“Û S+ VÞX*•J•J¥R‹Œ¡¢4MÓ’ÉdRÖ˜¡Ðë›”clvvvߟÍf³üÝä•J¥R*•JŒ1–H$p/ý½{÷îE£Ñh£!›Ífs¹\Ž1ÆÂápøÖ­[·ÌÒ”J¥ÜyÍ·Iê*y‡B¡Ð¥K—.1öfÚåâÅ‹áÿ…B¡ðá‡~ˆË'º‹ÜNš£H$‰¼óÎ;ïLNNN2¶ßg''''/]ºtÉM+€ÌJ”Ñ„+RðÓétz}}}}{{{[öûõõõõ»wïÞ=ÿ?þã?þÃèù<¼B/›¬d+]^©â˃ªÜx¾Ýß;m¯J¥Rùë¿þë¿æë3›ÍfÛÚÚÚø1????²ÅúÑ×××'ú-ÿž¢g¶´´´øý~?cŒ­­­­©äǘz ÀQÅÈ2‰ûQݯ†‚ÍÌÌÌðßiš¦=xðà™F§Úb±XÌãñx²Ùl–ÿn||| -lŠ=+N§ÝZ‰ÀUšŒ1¦"ìWWWW7666c,FíjÔcccc²+Œ}>Ÿï믿þš¿¯¾úê+YùxÅlrrrR¤0‹Åb&“ÉØMsTòäÉ“|>Ÿ‡Ï>|ø°\.—ÇÆÆÆŒÒ†B¡ÐW_}õ•Ùgssss››››@ À?#™L&ƒµAÓ4-—ËåÂáp> û÷ïßç‘\alÿ]eã¦VÏ·Sžz·×ððð°¦icŒ­¬¬¬ˆªõ€Üõx<ž\.— ;wîÜÁ¿‰Çãqþ=@àÙ³gϰ ÞÙÙÙ)—ËeÆ;}úôiwÞšêªø|>ßýû÷ï‹:w½ñûýþ–––(×_|ñ…ßï÷/,,,llll€¶´»»»«ëºžL&“ª+v3òù|~zzzš±ýNÏžJ¥R¹páÂXG£Ñ¨Ù*‚GeõßÛÛÛûã?þˆ'|¿ßïç%ÐÑÑÑáõz½Œ1V­V«`q1ÂNš£(B÷îÝ»‡?W±D"‘ȳgÏžÁÊHöY:NG£ÑhíÞ¢>t½‚ÿßEÇãvߵϷSžz·W:N'“É$cûw×_¿~ýºèw*õãóù|?~ÌˉõõõuÆ ƒAP|>Ÿo|||\”—¦iþ[rñ˜×4MÛØØØX]]]埃-õ$›ÍfAùÁ–Y¬‰¤â  »»»»V™ŒŒŒ`-2“Éd††††ªÕj5“ÉdZ[[[ …B¡Vùc¼^¯·£££ƒ±ýNÖÛÛÛ +ηß~ûmø¦išßï÷W«ÕêÐÐЪ¹ÝŒÛ·o߆w5sIˆ´eÆöWªŠ@ T«Õêüüü¼ìw@ °¹¹¹‰Ë£iš¶°°°Àç ƒ°zØÝÝÝÝÚÚÚ2+‡4(‹È´%3K6cš±±±±r¹\~øðáCþ9ËËËËŒí·ÿ]:NƒÛ >‹Çãqþ3¬ð1¶¯l y‰D"a^ãÍA&“Éx<ï‚U!^꺮wvvvZyW·Ÿoõ÷õn/~òïïïï7r šÕcâþy`×eOOOH¡x—Áõëׯ뺮ƒlÚÛÛÛEIfA<î b±Xlkkkóx<£yõ€àóù|Á`0h53Ðò;¨éñ`ßo.—ˉüöçÎ;&QðŸñ“ßÌÌÌŒ™0æ} _`dF°0èdš¦]*•JåÆ7ÀÀ»$ð{LLLL0vP`ó¸‘y“±ƒ÷ÒÒÒ69ãòôöööB|ÂÌÌÌL:NÇb±äe–¡{#÷Çììì,ß'ææææ@ f2™L,‹a33|ÆØk…±}xþüùó|»‹bgšì²*‹Å•••øN´` Þ…’Íf³P7õ~¾ÊïëÙ^ñx<ý¨Z­V/_¾|ÙhòW©^~`p¿´C±X,ö÷÷÷cÅ$Ùj,ŒÉeÁ)ü 7OÊÀ>y™™œW.°Òà6` \.—óx<žööööÏ?ÿüóÁÁÁAY:ð‡a9íuuuuõôôô¸YÎ|>Ÿ_ZZZbL= H¥R)°ž0&´aìu»V«Õ*œÉˆÅb1PzÀçˆórƒ@ìc´ú¦¦¦¦ü~¿L¬ssssÑh4Š-Q`2Y§°¢ë¶òZoÊårygggÇè7SSSSx‚ë`da«Åó­þ¾^íśߧ§§§Í&G•ú°©[d gì`L“Q,^Ñz<×ëõÊÊ c…1c×ÁØ ¼RÆ+™L&£ê«Èf³Y~EŠWÝ¢4V":­þ|MÓ4Üø•J¥ò?ÿó?ÿƒœfÌ[Œ‰a5Ș8H R•Õ¿]ðîìRq; FdDQÎÍž†p‡b±X¼|ùòeQD;cû-'Š«Õç׺òù|Þëõz=[ǧÛ­>¿Öå9Ìäóùü™3gÎ`¹^( §N:ewÂÆ—4ù«q›Vp'µÒ9±O‰±×46ÓàhNÆj§`à 3ÞÍ€•ÑàÀÁ&0Ö‰D"££££Œô³‰H¥R)g[ þ\×uýÌ™3gd+{ˆ»0S``ß,  D"‘H¥R)ìwÆç)0ö¦?Ð(Žža'ÍQ¬:8&V>F@Á»l`U‰•]'}×j\P­¯ L¾|”[[ɬ>ßì÷j¯F›õ{{{{E‹‘[A´ÄãI¶¥‘8È öI3v°BÀ>—Ëåd®SF£¢Ë·ß~û­[';ñæ-P:D«a‹©V«Õr¹\V=£À 8PåæÍ›7U:l,‹a!b´7‰D“GËò@ !nC£€ܦáp8üèÑ£Gü©~|§4G©©©©îîînQË€(F€ý`lßRƶ¼¼¼ «+YßÅ'hÊ& >.¨õï­";éέí`VŸoõ÷µn/€Ÿt›iKš›ˆ‚ÑÓétšâ—ò† ÀNpžêjŸ‚ÇØþŠŸˆ·w0öÚ÷c´ €†Å +&ø>ðÅçóùNœ8q¢\.—y®×ëõ@&“É躮»uB6W …‚Q@q+[1€‚¡úü§OŸ>åMpår¹ldaàÛ4‡····¡]D¡ØImµÃý ÙÓ‹Åâï~÷»ßñû¡aõ¿¸¸¸(SØÀ| ¤ˆ>ñ#²²ázƱø·²¶¨õïí€-w/;œø¼­>ßÊïkÝ^¿;ê(¹ ø…–){{¯·$¯©ûI€W®\¹R˽þx ‚xçÎ;°+@´ýðóÏ?ÿ<d]*•JŸòäÉ“áááaøl|||üw¿ûÝïÜò_Æb±˜ì^¾Žy¥ ©d‡¨Ôú÷VÈf³Y£O2™LFæc®Åóí–§–íuÔátãiùßÔàK Tý%õ¸ °Ù°{ œÑÅ¢üñïK½B™›.‰Dž?þ< …ðÿk•_:N7Û j‡GÉŒN=ø‹‘ ç«×Cò““Õ ç¸)v,ÆäJ€Hø€Bv˜î–gìøNNssss Tëqp\­Zãü°3âxÁßH‰çŒ7\øzHÄ‚;9žŒpP‹Ù9óÇ|Þ·ê¾|€Þ5Šâ…m3‡ñðMÓ4l?LNNNŽŽŽŽ ÔküIXÃÎVQ+ð"soï``¨Ò½°*Ç‘)Àq[¹Ù±ð cÄa³ˆØEtÊ×q3©B©Õª\4žÝºÚú¸A²hvŒæ^ÆüDô8´B–¬FÝ.8A#qT©T*•üÇüGÕóÖbtr$a7— ¢Ö‡Àj‚ ‚ ‚ ‚ ‚ ‚ ¿?‘¶&AćŸüI ‚ ˆ:Û°ðÖ+ØâVË­B°5 ò€í5µØRÃŒƒ·E—ívVî„·ìÑ6²Ú‚ëšú(AµàÜ „o؃ƒD7’©îÛæ';†(¯F E¼÷_ôÎü‰KFV Ø«YÏ=IJúÄïâóù|÷ïß¿p50l‰Åb18»?‡ïÞ½{·åšEq³sú%Þ›‹Ç‚ìsпpŸ}F¡Â‰l6›]Ì“H$üžÁ¹¹¹9|]/0333ƒ'‘H$yöìÙ3|íe8ÿøã?ÂaB««««pm#œ(¸´´´Ô¬{jñ5¶vORÃ'6ÛûÞºuëV8ÃÅ"üI‚ù|>þüùóÕjµF£õœˆá‚#:q’ ¢ÎÌÍÍÍá‰^vé ¬4Í>;ŒA€F'âïŒÎ‡ºhÔª¯whS+wÔ{5Žók´û,A{@â CvAðÃ0É'OþU€f˜Èd @3ãäÆI·Ámètâ%€ ˆFñÆe@ª€™Ÿ1ư©¸R©TJ¥R)ÆÆÆÆà÷`¾]___w^ìÆQ©T*‹‹‹‹Œí_–„ßQ…áááaMÓ4ÆÄ1Ä›„B¡P___üÝè> ƒÐ†ÍæÂ!‚PÅ–N§Ó KKKK¢ç&'''«Õj5™L&auF£º®ëSSSSî½q¬­­­ÁÿUWñ>ŸÏ7888Șú͉¿ùÍo~+»H$ùÍo~ó›Ån8¼å¡Ñ¦|få¼xñâEƬÝ舭‡ÁâBÄÑÇ’&ÿd2™L$‰X,ý.ŸÏçÏœ9sF×u>+ …S§N:l×ÓŠX^^^†wëêêêêéééQI×ÓÓÓÓÕÕÕÅc»»»»[[[[F¿Ÿ›››K&“IüY2™L6Ú~œÁÖ»«ÿÓ§OŸv¿dAÖPVB¡Phyyy"ûñ¶AQ }oooïQ1wïììì”Ëå2cû÷׃Á J:l:.—ËeÙío h‰v\0ÆX46ëê¹ð}IõF+£•¼]k.‹LÛ¾»»»[5AD­°pœ8ø[uE‡W*•J"…o¡Ôu]?sæÌXebËJ8¿xñâ~n°;IÖ'‚ j²À¯Âxuϰ[‡9yŽêŠÎìw‘H$òàÁƒš¦i¹\.'Н€vÈår9MÓ´o¾ùæ»çÔ›J¥Réííímv«P-ʉ•¿`0tËz“Íf³Çãõz½ŒH„ÈÐ ÉN4¥R©T­V«ŒQŸ ¢¾ü¤Ñ8®ˆ¬pÊÞƒD£ÑèË—/_ò§ñáXŒjµZ½|ùòå£XY `%oõ»FÝAµæÐ[b±XÌ ·„“種ÜT~‡ýü@ ðìÙ³gx Ä …B{{{;™}7ù|>ïõz½Ç“Íf³VÓÓA@AØåÐ+Àçóùpä¿êÊ ÿÎÈŒýü¢ïs¹\®YýçFÔ"*Ÿ±ƒñ*Ç;)§•²§A>H°AKKK‹ßï÷3¶ Þ`ö÷úý~KKK‹Ñïc±X,“Édðg™L&ceûÑÜX9 ˜ ÂM(ÀýýýýpÂÆÆÆÆêêêªJ:|¢×ëõvttt˜ùïñÉùW8*÷×§þ'ϬuÛÒm€A¸ Y,2222‡ÃŒ16;;;kuµ™ÏçóKKKKŒY»G@•ññññÝÝÝÝëׯ_wó¹V¹}ûöíB¡P˜˜˜˜¨åAEøvE;í!Ûk噸,““““´ú'¢™!À"pœÙ͆@ °¹¹¹¹·÷æýõׯ_¿®ëº®iš6>>>îVÙøksy B}{{{”˜Z±µz>œ§P( ·oß¾íÆ3UÛØ¨,>|èFY‚ j)ˆÇãq¸ çæÍ›7í®6‹Åbqvvv–1ÆÜ2ÝÂ%EXù¨gd9\bTO%Îops[$o-ãlÊñx<Îó<ã8n-‹ó6®8OqÇyžçãñxÜJ†Ãáp€¾ä9‡­äqœÍ™^Ó¶m»µ,ÎÛ¸à<Ú¶m¡¯ÙJˆ¿yÍÖò8Φ\¯×ë<Ïsß÷ýÖ²8Žó´éû¾Ÿçy¾^¯×µã†¾ÏøaE` yÇqÇy‡Ãår¹LÓ4ÁÊÝ0 ƒO¾¶¥išæx<aKÈWT3¸Ýn·yžç®ë:øÚµ/±=_|[iŸøöÑúÌÜq9š¦i°â5ŽãˆÇ)®<,áï¾-ÁÒ×<Ïóív»iüÀ€®Ykš¦á2àt:8ÿXnš¦ÑÈƒã  Åét:¼·Ûív:Nš0— mÛç÷õz½ZÁ¥Ë«Шp¼·Ûív¹\.P¦°Ç i¸‡tåîйhüàÙwòféNÅþét:ay¡œsŒçh}É“¾œ-äàfû`€½õà±v~XûùDOA5MÓp«tž¦i¢ö7àÿx<¯×ë÷w m±c¹±ÛíJócž+Å’U¿Öö…ú·ý•ò€"MÎ%ÀT.¬ ’pt†È ÒØ¿4 Cg…3Ôª0P˲Ì›£þ }±Áw¬àß0Ïo{Îm |fÜk“áeþÛív«aÜi-/\heÖ@Ýs"„7nX’[ŽÒzX:óÁ³fKZ0t µliÌ­B„WQ¤:G ),nV›s"CÚW¦ûÏ–0%ùcræÊåHW€b[]Ðk•¦®ë:°uÅ ïû€÷–ÁŸÏçœ4ãoœR€‘úãùÁ創\0tÅlžóïÑž¨Q.o¡= µ¾æŒ`Öþay0Ç`GS0œ[+œ†ŠE‡ZMÓÙö+÷õz½r…¦1,I'—d¨ý½VyÕ>@/†}®ówÚeoKº–(/ø.m?hlkJ债jÙ£ÏÍǘ;¬pãïÔH“*J {ÌO ieát:°LZjÉåBË÷-´]Á ýÌ8Ž£jù8—.î›Æ_I~HyL“àd¤Ç‚±ûœUÜT|@raÑÜîæùAK‘Î_ÓŽwÇÚ4M§à ÔÊ©Í(êÖ §àtAc‚A7 XºbšÚW§Žj JXÊ+„ÇõÛVà¸À8‰¦†E/æÀå…¡«;9­éªIêøu#íǧ[ÁÛ`ÿ}ß÷n~hýÅÜÅ~£íê´Wi;P­sôN+µäÀý|Æ0!Kj‘–ícé²®”䇔Çô7‰˜Œóü0àK÷„hÀáÕj¹– ÀÞ¶(%õ®ÈЭP2rÊ«4]¹àxSF@93œ5Û%Pã&@ºÌOw|²ÀuºäN8° \{ÁõP²i©!ÇœN§N#W6Pÿ¸tho¬‘Ú›cåiNÉBí@4}vìÔ˜M¹¬Bì,2¶”·îyàÓú‚£–ta†„æ<2þ=§AjNÔdoF€µàŒžbX-w·H÷ºÒ”›®y°©ÞÏóc{¡˜\)Ëûiš&(¸Žh¶î8#Kî\8Ž»ïßçx\ÍüÀ÷¯@¹à•ÖX¹ÄnQäÀõ8¶RZó€¥\çÉ—el-‡ã‡g–L¨!GN§Óénñ»ä!ä/¡ÕHË–à£2X9J‘‘ò VWà7íì)7ÿ×&&'½ß‚³i±¼õέhòI;øãðbn–€‹×ÃËår9ŸÏg<3Z[F(KÍ›õ{0Ó4M8M]×u¸_Ï™î¥Ó‹ÛX-Q¦E&¬(GûmnÉÛrø9K’µäÈ /sÓßrÒQ+-[•(°ÖSdRù…ÃÑØväæÿÚhäÄJ7{Ä´Z„5®¯©ø-ƒ?/å®6\¼ðíp8¦išn·Û +L[ÈøT “±2É­Lq}û=³dzñDrš¦©ÖНµÏpÛ<·Tê„(8á `ªCZBŽœ0pær²5µÒ¢á|>ŸkÏB°†L+žÁKš¨&¿ð`”Êœüßœ¸sá”A¬ HJVΰV‹‡«]ÊÝ*Ϲxñ·qG:ÉXS>èT×0L^:}±¶ŽÑ´û=r¹\.øå½%ÓKW½k¯öbÅÅ:.G•UÚ¡h:! îÐ-K’<S9´VNZpasƒ©5¹rpaà|¦iº^¯W¨ wî’œ?©üÐÊݵmÛâ«>¡èû¾‡†t»Ýn5÷5s䤿á6 -â¥o,¿.®ß–AËZççRñâo}ß÷PžÔ-N+W?qý¶æc=qk4MÓœN§]}¼^¯W®oö$±ôp“$l0š—:mÛ¶çóùlc™¡Ï,IoŠ%–þ%™´õúŠhè@KbMD\c–¸FŽãk¬rä¤%eeiMG®˜Ô)ĬFyÚxSáÆÜiâÚx-»ã®”–Œxš¦ ÏnñjØ Æ ^݈)5Ó‹¯µïw©² «°©ãJY«k2ËÁiÄ1%£FZj–æ)tâ’}EJ¦µÑȶ´`É÷\¤ð4·R8E2WyÄUÌ iÇQZÖ·`)«Ü8´àýq‹u6·×Ú˼8LK½·€Ó¤) ©im¸0c6×ëõÊÙPðÊXjE¥– U¬566t¦? ÃP²:T¼ :2‚á"‰]vCYRŽÒ0j,M ¶XrWW¶@#›å˜6­’;ë÷\RáaåPcEŽÃ³Äß©‘XÎýõp3G:[n)+«VèE;Sƒ%´;db TíeÞ–WJŽÅ¥NXgò`;:°UzÚž43ðšF€@Ñ*€öÞñØò{êl;Íü”ZbG£rÒ‚;æ’‹€jä)=)0MÓD;-ëU•Zùk¡‰oé‹€J¾ç¢ w©ûpgEÿ¶ÆŸ»Î…M¡³KYYd°"­ÆYŽD‚’øžÿpŸaõ[r1ÎÓ5ïˆÕÅ¥ér–þçù¡Ýâ6_º:d±{ íËc1w©Ûíhæ§Â%8¶MPš–Ô­OštÔƒ¦,þéò’õR!­üµÐÄ·ÄUÀ’;ë÷\4áÑG§báqIͳ¬0¥â·nC`èreÓ<<¥Ê…e)+‹ hÞQe@s'ÔÑÃápH-ÿo îO¬[7X1Ôö¿0ˆm} ¾éSVIzCÈ[úa™Õ!*§ÉRcXB|V‹3"÷JØrÔ#¶Ÿ¢IG-9è9.ËÝùk‘ŠÏz V~ÉöûRþ)ÚUz‡PbáL—ðcrjˆå‹Õ_-h‚:F•€Ô (kÐ9Y\/ž ¦Ê.ÃßbGKCØçc@Xé±* ¹é­±•V\öê²I'¢pG’4ËÝ©©†5¡ü9àZrÐßb7®myªEʯ¥Ÿ–Üi¿/埂ëZÊþ.ÆòL?Ý (9~ŠP,eµÄÃ2¸ýÄ®NµSª€íéÎ ½@ŒNhÛÿá:ÇŒño–m«5À ^NýÍM¯Ö˜vmÌ«Úew.îŠUIš#,!Gn\¾X¨)>s‹sÇq©û¦KÑä™åö=š×VwÚïKù§Pƒ¾X§*]elI,L‹e<οFjùm_B3ÀÇŠvU¯”9†æ-€Ûíáe?V $$[™Kü†ð D—ÌÀ×Nï’˜W¬•š›íkÂHeÊZr¤ÂÀ€¦¬=ÞX3-'T<Ø¢VñíO #åÓ8ŽcŽ¡Ík«;í÷¥üs`0¦ YŒ÷,ñãúj½ ¯`äžhÛ¶µÉÒ ]â—¶RaÖQb΄†Ó4Ms>ŸÏt;ЇýRÃ;0îKmñZe®•ÞÖLïÒdÛ8ûá|>Ÿ½ðœ=óÜ:PÀÖzüÇqrÀJí^·†ǹc`…*vÕªã8Û ½ãÀqÇDjKlkùç¹£½\í5…rçþI òï¼óŽ÷+Žã8Žã8Žã8Žã8Žã8Žã8Žã¬c²ž}v§ÉpPcP—a g0|Zò˜Ä‹/ßjš¦ÃH?"ø6ÚwQgu ájq§% @·øÁíƒp7ûÒwô·mÛJ7ìù‘È·¥l|Ksv4è½>îá8O•R`«Á¥mÛßÊ9MÓt½^¯>ø¿ÍJšã˜€» }ùÎqî _^Þ/[mÓ8ÎjÀÌã9^¹¨yO_»ä*Îc¯ÙZÇqt}¥sgX^g{jà½ÐT…%À÷H×cxÍÖr8νQº5E‰õ•œ-IŠÅ}wµ`Ó4Íñx<^¯×+½ôv»Ý4¯Nm‰õ( ÞÄVÀ9³.xKðÞ<Ój³ Ô|;[{ÝåsçËZ«mÛ¶ÜË™Ã0 çóù,)~{x…íx<é‹yðZ¥”–ôRY¸tÑg¡S2¯II?Yâw+™¥2È-›X_©¯ÁãÆéÒýcnØÇ´‘ã8.9ó;ŸÏgœ!ã8Ž©ÙxÎИÛívÃ3ZÐòr8jUlõ¿6P¬KYµ]úƒ<Ã0 ·×¬¡Ñ•0 êoëwØ»®ë$‹þ˜_kzéwîÄH2¯MI?YÚÇn!su€T_i »%ß:Þä¤A|^¾ Ã0ô}ßcÍ ,eÁÍ–Ÿ±#9'§xàôLÓ4á½iÚ)p ¤‹V¢cBœGæ‚ÙÄs÷±Jýþä%Ì$û×Ì3?㬶ñÇqÄm©iš¦ïû”s쯴 ¦À3sN ¢ñŸN§Χ®ë:è¼KÓKÛ7×?ì±()£¥Ëw ™k£éë,eMÝ‚"jÙzΪ_Ò»ßÃ0 ©ÎWìšÆxð§Gp°òÁ)xµ€SÚ¶má÷šKÖ0 ¡ËKÅ÷”xnoÑkeOø?,{.žµ¦”^*Ã’mðt:ð ÀõQ©øܹ榷mN7tYWŸâe()£­úؽôíZ,e͹…ô.¦Ìཀྵœ +õ/¡Y†æ–œ±†¨ñ»dæBáÑý¿%âzJ,U§¬ô}ß×X}Çq¬¡ÄàÙ?|[rŸƒ·ø[² â~žùåv¼DlQŽrÓ‹åY™âß4ýÀÒ}EImÕÇæÄ»µQ²¥9·PwëkÌ´´•Z‹eÿƒjH¸‘ÅÒƒ; n‰¥išæt:¨Ðõz½jŽ»Ñå›XiòOã¦Dæ­ürl9ûoš¦9ŸÏg¼”X&„3MÓt>ŸÏ¹ƒ5ýK¬ä¢!Ôkƒ©¥ÿ¯Jjž’ôâ:6ê3ÀRõJãç…6ŸA¡-)£¿kË|:N¥ñ–øÕ”# ¹…ú i©F­™–¶Rk‚Ö\ß ËvPÁ´†c1 ó¾ïû”ñP¬“€°ñL"–GšüK¹)‘y+¿[Íþ»®ë¸}ÚFvt hžíÊ7û–XO¦Ÿ IDATÀJ˜U‰«Ñ9RKÿZã½y~Û®¤$½8ÜVè6 ôÔm*̘í–®×Ч–”Q‰_,slu º5d.‰·Ä¯¦˜[ècS+Gª~«ÆL‹Zdæ„ADh–o`IŒ÷ˆR~Án¬8=Ó4M8_Ú¶mq%”f-P°ßXiò/æ¦Dæ­üJ¬=û?Gº7[:SçàV îiÝËår‰-MOÓ4Õ\À™uµ´ rh–þ-G§¨ÿ’ôâpCx3ද™ç74u› 3æ†n-jŽ‚›’2*-_«Ìð}«xKüjåM¹…¾6µ%©V°À%ü¸C«uý£%Ó¨{©cþÀ-Üe ߤŽWDZ`–B &—FfÉM‰Ì[ù•¨U'SÀï™§]8?Ô­.¬¾ï{nUàr¹\¸|„|‰-B½Ãy\"s* 1,~µnñêsd¯™^è¬ÇãÊê•6­,¸¥Žâ>©¤ŒJË·=nb€Wr$cË5ã­!sJ^[(ëX? VðrZîÞ]2­epaÉ4꾤’à<‰Í¦¤½K<(Ò¼ˆÉ¥‘YrS"óV~%jÔÉ`Ô‡óòv»Ýè1Ž9“X˜`;Ag®Ôh05ûè*@‰Ì©4”ø•‘Üàz1Ïé~Æ¿Ö5½óüÐ C}³Ê¦•ŸÛwÆHû×%eTâ7„Ç׿rõO,ðvÌVñÖ9%¯Æml|‰¥›%×â 3¨š¶%Ó¨û’J¢ÝcÂ3U¬in ‹É¥‘YrS"óV~%4ur΄úÍ5P\ Î~ƒsì©0N§Ó©dåDÊ«˜ÛT>kã”ÜÒ¥UM>Xâ×ú±¦WZ±ˆùµ ɆYí’¶5oJüXéåNLÌóÛÛB[Å[C攼Z·°¢#õ¥ª<*iÑÁ¿¶²%Ó¨û’JRâ4q©Ž…­‰Wr³UzKürhëäœ õ{/ €uP×* {¬XÑL-ýkŒ¥0j§/½ãòˆùµ€å‘–§cKÚµÓ«õËɆ·Žq ©­â­!sJ^‹[hÜ–*JfÿôJË%.!±Ü»ïì¡ÌPdÎ$„7y*tÅ FSh’›™·òËQº"¥¡d ` 4[·Ûíf¹‹`Ç1÷ô.#‹Q-ßZF€´¯IÉ¡1”ÚgIz¹ú¥w4qÄÜÒ Tš±ÒßRáÓ°k”oÊhnžß¶Ú*Þ¿–rÔº…úÒB(›ýS‹ÿ¥n Ör d)9*2gRÓ¯$¯äf2[ýR–Þû§HF€±Ç­j¦·ïûž;rÈrÆ}P§k¬XŽÅÑôÖ8HL-iÊyC¢$½©òŽùM¹ÓÈÂã=hx*þÏ)Õ[”d8¾Fë~ÍxKüZÊQã–;bÎ…á|‰:xÿý÷߇¿ÿøÇ?þQ#XÖ‡~ø!üÿ¯ýë__¾|ùRëßÂßÿþ÷¿‡Âw¾óï¤Ü‚ðóïÿûßðÛ×¾öµ¯IþðoàçóÏ?ÿ¾½c 'µ(‘y+¿”Ü:™Ëo~ó›ß¼|ùòå;ï¼óÎÇüñÿûßÿ†·¿ýíoøá‡.} ðÃ?üðÛßþö·Cá¿ÿýï?þøãßyçw^¾|ùò7¿ùÍo°¿ÿøÇ?þâ‹/¾ÐÈò“Ÿüä'_|ñÅ?þñ\*/´§Bxï½÷Þ³ø-iƒÀ¯ýë_¿xñâEuÍ’¦¿þõ¯…¿!ŸS”¤wüãÿøüý“×p¿%eT£|CáÕ«W¯àïÿ÷î·2—Ä[Ëo ~ñ‹_ü"„~ÿûßÿÞì9÷¶+:ó×ÌÌK6ÖñÁ’ Ö†ðr˜õšJ¼ÇS{I˜jfÚßBxû 4þ­Dæ­übJn`«Éž/Ò¬hŽ ZÑnËкYÒC°/ýSr/“ÊMoªýÆü¦ÜieÇ}ÄôšXþ•”Qiùb¸KĤüωWjc–xKüZÊ1åV:bÎ…Á†c9“ àúv»ÝÖº[«)hÚéå>;ŽÔMÓ4–{bƒã– é1(*G®Ì[ùÅäÔÉ%Á3uKã;‰Ü•…ÔE?Ú£‚ð@‹›«›%mç½å%4 bé1 Kzcm;å7å.†³ˆ)@¹eTêÃmÅÅò~«xsýZÊ1æ·‹Xÿ!Ö¯œ™öSë’ PرëO¹B¦g6-OFÒsµTƒ<\´i †üFååÞ§¿Dæ­üRÿ[Îþ%Àh°4œA>Æn!["qù Ã0Ðçqig¿å¶A¼ZRz4ŽƒÚWhžÖ¦—K¿„Ö­6< (Ìs|E¨¤Ÿ,ñ‹¡ï#Ìs|ÅÇòpl…Ío®_K9ÆÜjßëWÎL‹&RCWŸ¸p­n趇Ô)kî·¿Ýn7Ë12MZÆq¥å%Në¬%óV~÷6ûß3±A>¦Ô@{Ç>íœrÚ &Nî¶m[zêCãךޔRúl¥†Z©ÏsºN”ö“¹~1¸Ð(}[Å›ã×RÖ’[Ëãx8þ÷cîL+•É{QBx£±Óº\.͵´çóùL÷oc–âÚ4I¿ Ã0´mÛâ½èëõzí:zi9¸Dæµýî}ö¿G¸~ÉÙ?ʘ¨Ã0 ±m käÚµ†˜ìpõ2VTA©¿²¤W+‡Õm–+“Ò~2×/À=V”#3 íŸsâµúµ”µäÒ¨™p²õ WŠ´Sܹl-ËZx´Ã öKÏþgIèªEn=Æ{äÓ4M©åü’x-~Ká_ë“›Îú<·òñ:™ðךý;ÎR`»…R;3jSJâµø-Q´†Qr.ÅpÖå¹ †^'óÁÇý,—9ÎÑíiÑžä(‰×â·DÐþ9wÎsSœ2àÈ_êh ãìlØ\z⃆Û—/‰×ê7W°þÅø¿%žÇÙŸ|òÉ'ßÿþ÷¿o-ãXàÄßýîw¿«¾tCmI¼KËLÿ¾üå/Y«@H7­nœÉÖro¯8V†×l-‡ãXÁýýõz½®µ Xo®ßÜ;ÇqÇy:€˜ï]:Žã8OëÀV§iš_U¼µ|«š5®WuÇqœ=Â=,F·¶–quÀry­…Çqgm` oÛ¶…[ áøà³´Ý¨ôQÇqÇqÇqÇq,”!œN§³wœçÇÒÇ– _vnü~dk_ÀƒP¸ †aží¸UR á6$lÑ(…'½ª5Žãx>ŸÏœ=Àáp8\¯×+~Ü!vö²išæx</—Ë…{½ët:J_“òât:pœÒ+c–¸¸8a‰ËÇÔË[4ÿcù“çv»Ý´g`µå.Åé’â’ò)åNë/Î4MSN|Ã0 ©—Ȥ×ì®×ëõx<qÊÍ—\j…³Eøš°kÔŽr韱¹öOûÜTÓŸ¢9‘–ÓŸYú‰\¸0TáæF W‚!(\&jßÕÆ~ðC ܽȚ8¦išJÞ§q¶mÛâ9åÇ×ú-V¾éwÉ40Æfv4<­?ÜÞ±¿XšròÓ2ø§Â²ä þ-¶\žS4¼T^J”ä‡%lINøo¹I}U­þLÛOàíXI ÀFÝ´¿–Ò_UÀƒŸ$¤Æ ‡vÉÜh–ß© 5²â«Òc¨¢JorÁñA% )(4Δ`ÉÖfå ëº¯HàJ[î9±‚i´¦Üiý¥ÂÁ)vŠB·@ï—޲¦ä´¸Çqp T6η IOüpþh§OeÐÄK_®›œÁ_Ÿ&_¸¶[1£^€+‡œ´•øIÅã•âÐÄ›“Ο¶Ÿ·Ü ŸR¢ÐN.±&,Ý,½VÆY¾Iášý/ =C/54ª1ÇV¤x°{ÉUJ0Ó4M4}¸ÁH{XtQÊ? =CýérØæ/GNJm9¡C£ŒÅ—:x8±™WîÚôÕÊüÍwÊAãâËõ—›ÔMîà¯Ïš>Îú|rÈI[©Ÿœc€œÜæjÉó§é':Øã•7óàÄŽZ®CÔdJÍŒƒÊiYÀÐÖbÌ”ŽæçÏr„Ë:Pã¥`¸Û~Ã=cKýq†^4}TŽX~Ö,w»¥ýiä„vÛz™•Äwß÷=¾“"-[Ó‚|_ '¶w n¸SžÅZüYò@rS2øk㳦[‰±ø«•Ÿš´Qw)89$…˜‹#&¤[ꟵéÑøÓô4´­gþ4l)§išRÆšL©™qµÂǃ–Å Š;ò‡ã—òK³¬à‚æÂÓX£b…k™tß–B·0hú$™9#šµË}iZ9áw(ý?‡æ*` XêÓvÌíÉ[Óc¹/„KŸ%¾µýÕpC±ØùÔ”‰s·u~æ„“B ãx<¹ÕKGJ.˜àMÓ4Ñ6¨M6à›ÔOPh[´Œ[jA9… fÙ®ÉFɱ„ Ài¾Zhš­ÇòbîðöŒfß΢ÙÜ=ÛxŸúÓÈフ‘Ür×Ä·G#@€^…]+> ÜM‰¹ùB· 5ïg”Ä·¶¿n(ÖÛ>kÉĹÛ:?µá”ú‘¾¥âV4k÷g©~³È '(nžã72i lǵ²küi !µñi¶ ´rç¤ËÏÜøžÛ1@*›tŠ"7> ÜñÑÜ|‘î¡Hù[ºjù«áfžuüËêM-™8w[ç§6œR?RÒ7MX €íæÚýYªŸ±ÕnusO¥à¶°œ¥ña-O*X­Ü9é«yÁÎsºˆþƒg,•_®\KæKjËh½ç-m@vÜX:eM|kÙ”Úb䤭†) é›&®Ÿ_¢?‹õ!,t &¨Vpê&5ûιBqÉ«€5Kíxà™¸ës)š• \ b2Y¨%}¹WìâƒË8§Ü5ñíí*`úÔ“Ô© K|¸r°®PYV~RÂ<‡S½¹PòoÏOØàüãoÚ88wµû³X?‘ä«+X@íÒºÆMû{ Hº !†t 0„7CJ·¤s[FÄ T£iµU©Œ4 †SŒbaJ2kâÛãc@ô7º_#>|¼T‚{L“êÎSh¸íqŠ-·/IÓ§±cÁ/|·Ú÷hέSÙ­nh~húÍÝ÷Ö{8#Ü5îcˆ¹©¹"¢ Cú¦‰CšÐÔîÏbýDîM€IÆñá©Ü®ë:z ^ñÛÝhäàF3ÈàA‰žçžQY°ÍsÀ>ú0„‡ýá•n=kš¦9ÇXã¦7÷IÇò$Í‘V„XGOóÑg}S÷ Ã0м§2J·#RYÚöá Zì†VVk¹Kñíù9`îwéÆ´Òø.—Ë…{Æ”Æå@ë UpÝÅeOëŽö!S À<¯ ¾?üqrÆò8× ½r8u58žIã›·é)÷x m÷%@“ÿÚ0¤o4ÚÒ±…öõµú3 U¯SùÖ¶ßPgP 08ÂÁš·öRÔíM\F¥ž}Œ]|ƒVJ­?I³Çƒ#¦ÉY4ËÞVÒ¤ø¸Y…UFm¾ÄŽZÊ]ãŽË#ŽDN|)9¹x¸#t9crÇàÊ›­sþhÙÓô8÷±ô­q½Æ_ÊC‹¦üðà©Y™ŒÉ­u7Ïq…¸ö› ±r¨â)¥/ö{ÌŸçFŠ/¿”7¹ý–ÔO,Ìh/—Ëe†Ò0 žUJhŸæâ‡§†ifŸÏç37H ïy\¯×+/¤ïz½^i±kSÛ¶máš' ›&OèŒVbþ°œ–óôÕ4M|‡Ãá@ÆüÄ*ø4M“öܺ¶Ü¥¸Æ‘}Ž“3&uKŸ„6¾Ô 9•g4ÿ¤z­-‡Óét¢ûÃ0 ±úBë}Û"•>ºê¦iµ^ij»˜œZRñ¸Üc§¨¤ôJásçÝ¥r§á,ý ‡Ôæ¥üÔ“™s#Å›þ—™¦-åôgRXÚªE±FŒ5—¥drÇ©ôU®{féeóK ¥,,ã[ Ñ·âKµz÷Ýwß !„O?ýôÓZa:ŽãÔ¯i-ôGËoûÛ߆ÂÏ~ö³Ÿm-KŠlö¸aIëë_ÿú×Cáã?þ¸–pŽã8µùàƒ>€¿_½zõjKYœ§Ç·¾õ­o…ðfR|—h÷VæùÁJ}š¦Éú˜Žã8ÎŒã8Ç#žíÓ.ÖãÂ÷ÌsÝX‹™áI×/|¼!ç9]Çqœ¥à:dÚ9W½;}縰,ÔX¿§â8Žã¬>Ń'ëëŠOWÇqÇqœ·Ã>ëKzк56xONs]¦¤iºê8Žca;¸¯Dsÿ)âyâD‹ r—Éà;Íñ¨xãkhÅäp`_´mÛâë+}ïËÙpEqóøÿÖrm‰ç‰#ׄæVð«r*¢+ûAºRV[þ޳49“Œ§Žç‰nõ³. ÁLPs}­/EÝ?P†x«‡Þá8[cÝf|xž8Žü¨O«ñ‹†) Ðε·•í=â´Ôʈn,¾{ÊÏ5¹§|Y»ž­ ¬hIÏûR¹sXú‡séÒ6^2O}a*¥¥Ó¹`kVU¤K)°ÍwÎö;pù™“/øA,I¦ØR÷/kÕ3Mý[XÕ†aÀÏÞ‚’TsekŽP+Ž{doub/ñçí´‰¢îžš¿Ð9—Üî'¥:e¼G‡;ŸØ>„}8ÜÓ›š×ý4ß1%[{ÏÏÜ|¡ÏyžN§S×u¼0–JïÞóe­z¦©¬¯ÆžJ®mÛÂÅaIsß÷=½€^8=GIÖ\œÜ¹éÍq§ù®!§ä'7Ý[ír å©ùÓPrLÒr4òv»ÝpgRlP ilÛ¶…N/&¯”?š|+5¼‡üÌÉî}pè\SOH‡pùÿ_ªžÕh·–wÚ1p²êö4=<7¾´a«6ÍmÛ¶øY^mÚrýåÊ™ëOr§ùž›6ŸÜtoI²?È-”§æO tΖ}‹É2×0siJ>¥ülš¦¡o‰ÏsÞÌñ)åK¥íÛTàwëCx('|•y=©ËЦ9vålÛ¶íét:›þrå¬åo/á.ÏR¨úm¢r3ã^ü9N ð#4¸Ãõ£‘6JÚ-ÜE2ÏéU”=DiÒŒ›”½·5gõ—+gM{ ÷IŽ%K¬{òÇ1 Ãp>ŸÏš¥YDZ€-oçù‰¿Vm{çÀK÷KȶRšñûx ßv®?‹œKùÛK¸Kų)ÚDåfÆžü¥ Ë¡mÛ¶Çãñx½^¯tïlÇñr¹\8ƒ)÷Çû‹å½¶L÷ì#+Îlió7 úWã)ú£yÞu]‡ ù¤H±fîìþt:p½Æ[\]ê v£úëHPoj…]Â’2丩™6K<œ­H¬ÎA*nàGš€.îG›y¹™¾gtÿzžç™;ל‚.©¹?»?m™îÙ_oÎÔÇà”£”?éŒþSõ‡£GcþJïSˆÉ‰·æyY0âkٱͅk5ýi©åO#“ÖO±xèi ×ú¾ï¥Ó%8ÌÕýÐDædƽûÃJÖàÆqO§Ó‰ë°iFãLu¼?жLïÅÖ¸Çq±2‰; º€ý]¯×+>Žë&ÝÏ~Êþf‚ÖŽËjs¡•3U´õ%æožß¾h)„Ç}Õøà‹/¾øBëï£>úè+_ùÊWþð‡?üáÓO?ý”ú}ñâÅ‹ýèG?¢[1¹þî‰wÔŠç—¿üå/5î ‡Ÿþô§?íÉOá~fäKú“¶b†î/î/UVZöèO6uçþ¶ñ— §ÄÆM×uìû¹²‡ðædá§–ösü-™Ç9nJòÍ"ïZ²­åÇä17a÷à3¤g¸%£¾ûãýqhÜÜ‹?mØÔû«ëïÓ[ŒsãËu+gxÉ]+C ÜŸYlw´þr嬕w¹uÅJN<5d[ËÉcnÂöì/v /mc‹ÎT|î÷—ëæ^üiæîÜ_]¹ÇsãËuÓ4MC­ )rW´þrå¬vn]±’O ÙÖòcò˜›°=ùK—½4ñÑç_ÝŸìCãæ^üifžx« fXî÷‡ó\ã¶îr/²Ê« 75ý­-ÃÖaS75ó­4ê†;á’b-?!„ð¥Ï>ûì3­ãB÷÷âOãîÏþóŸõ«_ýêç?ÿùÏ-qüà?øÅ½û{ºüç?ÿùüý½ï}ï{œüÜ»?ÞŸô»ôý_ÿú׿Báç?ÿùÏÁ¸í»ßýîw9V9÷ Þ’³æúã˜,¿Kä\î”V?ÿüóÏsÃùç?ÿùOøûý÷ßO~BHKºø˜ 1¸Ö `£®ÒqOáº?ÙGnÙìÑŸæ9λÜsòçOÝÎsË=!ä=d-¿X]иáÀÛkÒ@‡/(’À}%œWÏõÇaÍ-48CDºe«•‡ 'ö;¾}ÒrÄ”º±œÏ‡qm-?÷p3ßÚþf‚%¾òžÆrÃ0@' ñ-]¯ñöÅ8Žc̸ör¹\¨’p8’MS®¿šéÓø£ý8^Î>ŸÏg® räðÏçó™.™Ç#އ»œÊ’FÍ }ÿkû !ð³6Ê4½ýœé=øÃ¿I…&»âáÂwoû“ÜǸ'š¼™çíïØ¿ø7<3ÕÆÂãm ­œÜE@šðcñYòÉUÎsýqhÓ’ë/–ôR®<Úü°žÒÜìö-Ìét:q¯u¥n‡Ú³?©@´œN§®·Ûí†_U’ÂwýÍÜ“?Lßç½–w<œ¿Øi‹§êæ¯ö5@ \XE•qÇóù|æfºô5ÀaéªáX½HÉLÛöx¥‹[aIDAT×4M/qR7Ó4MR~æúã°¤%×ßår¹À@6MÓt½^¯t5ƒ+­,pÿåESM<17ÜåiÓô𦀴ⲖÇqœÝ;È, îP·–K»kÉ­¡8o³f;ŽãlÆ l(¸·ërÁ,¶%R W¶a‰2ö·Çq|ðÁÀ߯^½zµ¥,”o}ë[ß !„wß}÷Ý­eq–ÁËØqœgÁV³ÅqÇãñxÄû¿°§ òXž _’™a/²9uð2vçÙ±•Àu¸´óÝ‹!Õ0 ¶øÆ†rÎÓÀËØqœgÇV @Û¶-÷̵ÅJÞqÇqÇqî¼ÏWé:6`æ`yˆd)à6´½Y,K³»­f}κìµ^.Á=ÔuMŸõƆ=åùî€#.ãkæÙöªóæË5Žâh²ÜÓòå=tŠÎ²ì±^.ÁÞ뺦Ïz*cÃ^ò|—ÀîÍkàÿ[ËuO@žíÉH®OÞSYî½St–gõr ö^×5}ÖSö’ç»nÂZh}©W“œ S×½nÜaîeéì ¯—Û¢é³|lpD¤ ä »>°OG!¹F»=øå¹Òk:ñc'±=YXŠÝÓvÓ<·>æ¹¥×¹sn·Û /!ÁÒž_V £išær¹\èƒxPÇOðÎó›çŽA÷¼ÞPJ¶€ðk—Zƒ¬y–²°›aüXÓ=ñÜú˜§”^èßèD[˵ðJ*Ý>á¾KyUš‡oÅE4@l Ò4MsÏF"Jó-„‡Æ{ºÜrp8œú*Z Ùp8ÒïMÓ4]×uð:#•k©|Ó°V<Pr;j(çØàoioRZ·²QÉ-kÃ)Õ±×ÿ¸W ¯×ëµt;oéôZÃ_Ú=…> žr_›Rùi’›¾ï{¬àŒã8ruG:ýÂ}—â徇­ïð:kRK&I™ ‘HÄ2w¯ÔÈ7¼tëôimÛ¶PḣT5dãâµÆ³T¾iX+Jî7íñÑœÙaÓ4 à¶ZF.)mƒWQRaw]ץܖç]:½Öð—vOíŒy~¨sk¯>•ÊOÃà~Ç}8…Ö¨ktÂÆ}—âå¾ÇÒ«Åb\šLÐP²'Y÷ÔÈ7¬±m-Ûé5\8’;xGüt:º®ë൪Xܵê[Šñà7èá=mM'‚vðк/ÙƳ±½Xp[ËHÓÇHƒNÓ4Mß÷=eMÓ4N§^hš¦ÁáÐv‘Ëé- i÷X¹ÚÃÖSNŸóö:Ó4M¸Mö}ßCÚÁ&Ú(þ&}Çý(nÓÒ÷a†¾ï{œÇ‡ÃáõúV†œL©Í–qçR#ß–ÊûœpaÇq„·W4«RÜkÕ·Òx°1foÇ<-ì¡­c–Çfj%©µ÷¾tþ[Ãß›û¥©-(‰Ü]p¿Ô1¼‡jî;î{°b!}ë/+à i/ÄB|[*ïsÃÅZ#½ùkž”ƒ”mG*îµê[i<Øðf€°{¯–÷{hë˜%äÁ³ÎÒ°jË·tþ[Ãß›û¥Yª<5¿Kqsß¡ï¡+uÒ÷˜!yR†ÒL§2±v¡Y.å o†a°¡ŽF¶T"Û¶m¯×ë:-'­±x­~4䦧´LñÄaÑ8cq—ʦ¥F<ÇãñHë0há°„¦‰'U¦Kù©ñûñxmzrdË9˜“¦Ùé¶=î{I<œQ )ƒKZÀ_*½©òYÒ/Ÿru û×,c÷9'#4i¢Ð=O<û’,­ñÌQ2गÖH3I|I(Hx5 vt*½kçç’îq9YŒfcå»vþàöÛrœjØš¥ˆm c;1k¦„ð8c´Kk¸qr7ZåÊFÝäø Á®ää[ôiÜÔ ‡“㧤L9,ƒI<¸¼ñ,.ÓÁ”6žœò©á‡B;e:Xaÿš7 ¨Õ1Ü ¡í¬eDoÁ¤ƒt,<<«ŒÝy#É ÝʇƒÜô®ŸK¹Ç÷ghëRéò]3è6RLáÐÞ¸4¿qÛN¦×Z –ð#…£‰‡ºÉñX”€œ<¨‘>›²áprü””)Å:øçÆ‚¼MÂcVn«A 3§|jøá …5 C÷isäÅГFôw\vtPH]R…ÌÖßðï’›µó³¶{*/§dÆH•ïZùCÛÇ_x­†µ,áGS¡´áæøÁH†ô{NÔHŸÆM ÙrIÅ#¸§ QúžÞÚJrö)ù-åSÃGM »8ÉÞBœg~V‰—<¹™x,>­,K®„°^~.ážÊjQ4åÂòùCÃæ üúHG­[$%èJ…„€ÊÑÂ~¤p4ñP79~_('wŽlk­àY ´¬¿ƒíËRåSÃŲ Å!VØÜ…$’aœ6>º4Û[¥Kù` €m7RF½1Y %€Ør´%—ÌÏ¥Ý[·,å‹ýÔÎ: §fþÏVpW´²X—XÃÀ’×P‡—*Ÿ~(¸ ¦Œ¥8´Pã¨y9¹5×òÒ­< I3t­,KžˆQ3?×p•ÍÔÃXÖò娑?X YzàÞ 9•2ç@솮Ü£{BÚôP7¥§4éÏI_®›²å’Š»D¶ÜSµd‡Y$î¤R~rêh-?ÖôÕ®75⣳,‹õ7½Í¯´SÇ÷P¥âp8JïHQ»üöྤ|­ñÕο'AN¢-÷à=ÉHGŠk†Ür ÕÆµé¡nJïâI‘#«ÆÍ–9w©l9÷hÃÆ+[±¸ñì9§Ž®å‡²tÙ`pÁtiâ£Górpl»Sz)Ëå²üM€5òsoîk”/P#´¯³nõœö"äVJÍM€Ð)áo´À¡àBx˜aá›±aÈ4MVÎçó+†&=œi_¢1[ÂÈMO©l¹¤â®!¾ñOúž®Ót› :+ZrêèZ~(µËfÇ‘;†E÷€sgÄôh“F&<Ë”V,kÔËšô.™Ÿ{so-ߥó×ü”tí_®“’õn˜`¿K¿BüîçZ[±K–È7IŽZnjȦÅO-ÙRe–(Ô€ìv»Ý¤8­utM?RYåüs/÷5îqüV÷ZYk¤½VþXÓ»f~ZÝ—äy-yJò'„øÅ>óüƦ¤FýØ–LÂÄ^Lm ¤^.—ËõàóžT¶˜¬\š·Ê·š²Æò W¶%òaMÙJâɹØÃRG×öÃåIÎïx»c†+õ —tlS[V÷1j§]ƒUþ½å§Õ}IžkÂ_:€¾ï{º¥6 Ã;è UÇqê‘êkçV³¤a†¾ï{ü€îÌŸÔþíÎY£¾9O¯ ޳,©‡Mj6=%{ö%ÄfyÓ4Mx_×YŸñNÌ~ÃYúþ‚+‹+ŽSi&\rÖZOî….ÎÓóÃl-›ã8Γw¸Ã0 KͨQVêQçùOkÓ,[Ëå8Žã8Žã8Žã8Žã8Žã8Žã8O•¶m[zNÚ÷oÇqç ƒ_;ÄÐ×öÇqÇyBÀÌŸ‘†»°ýÆ4Çq§üfvn0PÑ»žáõ6¬`6K:Áídø4¸åv»Ýðl—;¦ÏDB<4Þ{E:’SrT?JSëòü@Ç“zˆÃqç^ÎqJßÏçóÿ&=Å©áv»ÝðÒ4,_ã G@Q†aÀwƒ6~öƒ¦i¸Å ‡ ƒ=¾FtzMn~í0'Àþð–Bí›×@ HmO€²¯‚iä®I,>P:ŸÛh{nŽã(‘:Pî; ¦ð¬* Âô-çd˜eßn·÷ž³&–ÀÅ•Ï-â_#¾ØßŽãàFIë@»‡€% 2]pÇÙˆ5fyNÙ)€-l¬§îoŽã<;\X‡Ò{Ö>`½àÁéö6à8;Fê¨|+Ãóojܸæ=5nÔ~«!oŽ{WçNp`<ÿÖò¹ä-€µn´¾«ÔЭî994a:޳2RCö¬ Ï¿õ¨õàoXW\pg ¾„_¡ûì³Ï>ƒ¿¥ï޳G>ùä“OBáÅ‹/JNC¼|ùò%þ·6mÛ¶/^¼x™cpíPûÍq'Æ—~øÃþþóêÕ«Wð·ôÝqöÈG}ô |?ýéOºV¼°œ¯ÙËálŸ}öÙg}ôÑGðìè×µßÞAhd[Ú½ã8; ’èÒ©ôÝÑã[ëÖõølýÒeÐ4M3Ïüë•\ܰüOO+À;x&ߵ߶ķç€3Ôt?RúîèÀ–éÏá¬÷^€z ·û͵âÃGúRñÁ‚\}wtû‚k‡Úo[â €ãÜ0#¡ôÝIÓ4Mƒ_þ{j¯ïí˜‘à »„À…«±ø`¦³üßfˆýpíPûmK\pçY uúóÌ/ ;÷Í0 ~‹¡Æ5ÌmÛ¶Xi¼÷zã €ã8Ïià§—Ò8ÎsÄÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqÇqçmþ?%L†+|mËgIEND®B`‚CubicSDR-0.2.3/font/vera_sans_mono36.bmfc000066400000000000000000000020101322677621400200760ustar00rootroot00000000000000# AngelCode Bitmap Font Generator configuration file fileVersion=1 # font settings fontName=Bitstream Vera Sans Mono fontFile=VeraMoBd.ttf charSet=0 fontSize=36 aa=1 scaleH=100 useSmoothing=1 isBold=0 isItalic=0 useUnicode=1 disableBoxChars=1 outputInvalidCharGlyph=0 dontIncludeKerningPairs=0 useHinting=1 renderFromOutline=0 useClearType=1 # character alignment paddingDown=0 paddingUp=0 paddingRight=0 paddingLeft=0 spacingHoriz=1 spacingVert=1 useFixedHeight=1 forceZero=0 # output file outWidth=512 outHeight=512 outBitDepth=32 fontDescFormat=0 fourChnlPacked=0 textureFormat=png textureCompression=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 invA=0 invR=0 invG=0 invB=0 # outline outlineThickness=0 # selected chars chars=32-126,160-255,262-263,268-269,273,286-287,304-305,321-322,338-339,350-353,376,381-382,402,710-711 chars=728-733,937,960,8211-8212,8216-8218,8220-8222,8224-8226,8230,8240,8249-8250,8364,8482,8706,8710 chars=8719,8721-8722,8725,8729-8730,8734,8747,8776,8800,8804-8805,9674 # imported icon images CubicSDR-0.2.3/font/vera_sans_mono36.fnt000066400000000000000000000705321322677621400177740ustar00rootroot00000000000000info face="Bitstream Vera Sans Mono" size=36 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 common lineHeight=36 base=29 scaleW=512 scaleH=512 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 page id=0 file="vera_sans_mono36_0.png" chars count=254 char id=32 x=107 y=296 width=3 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=33 x=101 y=296 width=5 height=36 xoffset=7 yoffset=0 xadvance=19 page=0 chnl=15 char id=34 x=230 y=259 width=13 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=35 x=63 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=36 x=481 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=37 x=396 y=0 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=38 x=416 y=0 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=39 x=89 y=296 width=5 height=36 xoffset=7 yoffset=0 xadvance=19 page=0 chnl=15 char id=40 x=430 y=259 width=10 height=36 xoffset=5 yoffset=0 xadvance=19 page=0 chnl=15 char id=41 x=419 y=259 width=10 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=42 x=52 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=43 x=415 y=37 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=44 x=501 y=259 width=8 height=36 xoffset=5 yoffset=0 xadvance=19 page=0 chnl=15 char id=45 x=324 y=259 width=11 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=46 x=62 y=296 width=7 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=47 x=224 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=48 x=440 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=49 x=69 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=50 x=86 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=51 x=103 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=52 x=278 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=53 x=120 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=54 x=314 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=55 x=137 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=56 x=154 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=57 x=171 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=58 x=504 y=111 width=7 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=59 x=492 y=259 width=8 height=36 xoffset=5 yoffset=0 xadvance=19 page=0 chnl=15 char id=60 x=386 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=61 x=404 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=62 x=422 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=63 x=159 y=259 width=14 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=64 x=0 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=65 x=180 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=66 x=458 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=67 x=225 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=68 x=432 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=69 x=188 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=70 x=205 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=71 x=324 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=72 x=222 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=73 x=209 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=74 x=193 y=222 width=15 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=75 x=0 y=74 width=18 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=76 x=161 y=222 width=15 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=77 x=180 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=78 x=144 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=79 x=90 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=80 x=239 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=81 x=72 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=82 x=19 y=74 width=18 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=83 x=36 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=84 x=468 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=85 x=432 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=86 x=38 y=74 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=87 x=273 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=88 x=260 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=89 x=80 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=90 x=396 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=91 x=462 y=259 width=9 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=92 x=270 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=93 x=452 y=259 width=9 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=94 x=57 y=74 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=95 x=84 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=96 x=408 y=259 width=10 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=97 x=180 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=98 x=494 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=99 x=81 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=100 x=256 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=101 x=54 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=102 x=65 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=103 x=273 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=104 x=49 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=105 x=0 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=106 x=174 y=259 width=13 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=107 x=252 y=148 width=17 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=108 x=290 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=109 x=76 y=74 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=110 x=496 y=0 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=111 x=18 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=112 x=36 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=113 x=307 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=114 x=497 y=222 width=14 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=115 x=129 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=116 x=341 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=117 x=80 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=118 x=144 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=119 x=315 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=120 x=133 y=74 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=121 x=320 y=37 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=122 x=64 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=123 x=48 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=124 x=83 y=296 width=5 height=36 xoffset=7 yoffset=0 xadvance=19 page=0 chnl=15 char id=125 x=16 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=126 x=288 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=160 x=111 y=296 width=3 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=161 x=77 y=296 width=5 height=36 xoffset=7 yoffset=0 xadvance=19 page=0 chnl=15 char id=162 x=449 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=163 x=360 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=164 x=417 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=165 x=100 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=166 x=95 y=296 width=5 height=36 xoffset=7 yoffset=0 xadvance=19 page=0 chnl=15 char id=167 x=401 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=168 x=312 y=259 width=11 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=169 x=189 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=170 x=202 y=259 width=13 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=171 x=358 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=172 x=18 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=173 x=384 y=259 width=11 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=174 x=42 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=175 x=372 y=259 width=11 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=176 x=348 y=259 width=11 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=177 x=296 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=178 x=299 y=259 width=12 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=179 x=360 y=259 width=11 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=180 x=482 y=259 width=9 height=36 xoffset=7 yoffset=0 xadvance=19 page=0 chnl=15 char id=181 x=414 y=148 width=17 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=182 x=396 y=148 width=17 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=183 x=504 y=148 width=7 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=184 x=0 y=296 width=8 height=36 xoffset=5 yoffset=0 xadvance=19 page=0 chnl=15 char id=185 x=396 y=259 width=11 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=186 x=244 y=259 width=13 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=187 x=375 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=188 x=95 y=74 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=189 x=114 y=74 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=190 x=339 y=37 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=191 x=144 y=259 width=14 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=192 x=120 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=193 x=436 y=0 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=194 x=60 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=195 x=160 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=196 x=20 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=197 x=336 y=0 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=198 x=252 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=199 x=433 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=200 x=409 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=201 x=426 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=202 x=443 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=203 x=460 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=204 x=96 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=205 x=112 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=206 x=128 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=207 x=97 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=208 x=220 y=37 width=19 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=209 x=332 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=210 x=260 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=211 x=242 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=212 x=206 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=213 x=486 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=214 x=450 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=215 x=477 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=216 x=231 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=217 x=414 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=218 x=378 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=219 x=342 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=220 x=306 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=221 x=140 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=222 x=494 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=223 x=377 y=37 width=18 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=224 x=252 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=225 x=234 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=226 x=216 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=227 x=126 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=228 x=90 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=229 x=72 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=230 x=200 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=231 x=17 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=232 x=54 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=233 x=198 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=234 x=108 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=235 x=216 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=236 x=360 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=237 x=0 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=238 x=162 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=239 x=324 y=111 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=240 x=234 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=241 x=177 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=242 x=270 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=243 x=306 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=244 x=476 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=245 x=368 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=246 x=350 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=247 x=453 y=37 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=248 x=0 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=249 x=305 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=250 x=321 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=251 x=337 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=252 x=353 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=253 x=434 y=37 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=254 x=188 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=255 x=358 y=37 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=262 x=145 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=263 x=0 y=222 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=268 x=18 y=185 width=16 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=269 x=465 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=273 x=376 y=0 width=19 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=286 x=170 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=287 x=35 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=304 x=32 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=305 x=152 y=74 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=321 x=300 y=37 width=19 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=322 x=396 y=37 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=338 x=40 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=339 x=294 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=350 x=108 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=351 x=33 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=352 x=126 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=353 x=113 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=376 x=476 y=0 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=381 x=162 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=382 x=241 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=402 x=456 y=0 width=19 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=710 x=258 y=259 width=13 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=711 x=272 y=259 width=13 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=728 x=336 y=259 width=11 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=729 x=70 y=296 width=6 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=730 x=441 y=259 width=10 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=731 x=18 y=296 width=8 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=732 x=286 y=259 width=12 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=733 x=188 y=259 width=13 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=937 x=198 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=960 x=21 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8211 x=147 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8212 x=210 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8216 x=36 y=296 width=8 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=8217 x=27 y=296 width=8 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=8218 x=45 y=296 width=8 height=36 xoffset=5 yoffset=0 xadvance=19 page=0 chnl=15 char id=8220 x=257 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=8221 x=273 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=8222 x=289 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=8224 x=369 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=8225 x=385 y=222 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=8226 x=216 y=259 width=13 height=36 xoffset=3 yoffset=0 xadvance=19 page=0 chnl=15 char id=8230 x=472 y=37 width=18 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=8240 x=168 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8249 x=472 y=259 width=9 height=36 xoffset=4 yoffset=0 xadvance=19 page=0 chnl=15 char id=8250 x=9 y=296 width=8 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=8364 x=288 y=148 width=17 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=8482 x=356 y=0 width=19 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8706 x=324 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8710 x=126 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8719 x=0 y=259 width=15 height=36 xoffset=2 yoffset=0 xadvance=19 page=0 chnl=15 char id=8721 x=342 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8722 x=491 y=37 width=18 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8725 x=378 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8729 x=54 y=296 width=7 height=36 xoffset=6 yoffset=0 xadvance=19 page=0 chnl=15 char id=8730 x=280 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=8734 x=105 y=0 width=20 height=36 xoffset=-1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8747 x=240 y=37 width=19 height=36 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 char id=8776 x=450 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8800 x=468 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8804 x=486 y=148 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=8805 x=0 y=185 width=17 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 char id=9674 x=392 y=185 width=16 height=36 xoffset=1 yoffset=0 xadvance=19 page=0 chnl=15 CubicSDR-0.2.3/font/vera_sans_mono36_0.png000066400000000000000000001052451322677621400202100ustar00rootroot00000000000000‰PNG  IHDRôxÔú IDATxœì½_h[I–ø_jæQÂòƒeÏ@['0l§-›fÙŒ»ÇXîÝ – ,ù³xˆØí ëÐÄ ì‡í/Îl–Lz!–/=É.½X&ðËx{L?Ìoå ¤=QçúÁzpK þ=¤kúääTݺ÷Ö•®íú<ÙúS:·þž:çÔ)Æ ƒÁ ••••@&“É´Z&cÉd2Y¯×ëAh›B¡Pà2Ôëõz2™LÊ^×Í~j0 Ç•x<ñâÅ‹T*•‚¯/...®¬¬¬´J.Ã˶YZZZ ‡ÃaÆ«T*•ööööb±Xlµl­À(ƒÁ ‘¹¹¹¹X,³,Ëêééé …B¡l6›eŒ±T*•2–€ÖÁÛ†1ÆJ¥R©¿¿¿¿V«ÕZ-W«0 €Á`0h"F»ºººclyyy¹Z­Vc¬X,K¥R‰1ÆN:uª•2W …B[eJ¥R)N§[-“Á`0ŽÜüpppP( ð=`Ü­áôéÓ§[-ƒÁ`04…h4=qâĉVËqÜà ý‹/^Äãñ8cŒe2™L«ÎdD£ÑèƒÀÀ8¬À Ç Å µv8˜á 7ø‹‰ªþ%,ª^_*9®[;`™p×ëtLÀ±tTvÇT”9çÁƒ¢ÑhTô]\—/^¼ïóv€¿é&jÜ©à·Â êvõ'‚/~D×ãþŒë?—Š ”ìÇyîk x  ‡ â¶C™ íd“âqÛ¥P æÅ‹/>ùä“OT' YýŠ€õŽû‹à(´'5~TŸÏ©Àç§ÇÆD $žã¼(8v¨öo'›./í¡ã7àïPu'Sz©þÐÔ5G¤‰i ú!£H£Æ ¤zÐÕI[©èðÉ ¶osÝZ|û»ÊäèFvÙ„®Z&¬7'm‚¢£°³‘-âN,TÌ,{{{{ûàààà“O>ùäÅ‹/,˲ê?à—ç?ѹu·cEµ:Utµ‡ ; š[+@KvÿvÚŒŠàÍ@Õlé¦ÂD»|™eà¨Ð,s™ °/ŠÌÎ:g?û’ÜìÒqßáF±Àuî6qÉQTdõédñ²SøÂÏËþüóÏ?÷[€Ï&²¹u‹ªÊä´|]í!BEqu¥j Öaݱ׎Æãñø“'Ož$‰„Ý—ÃápøË/¿ü²Ù N´Á“9„íííí•J¥Â˜»'Ož<ÉÿÞÙÙÙágEù9R˲¬ÍÍÍM}OÖz¢Ñhtxxx¿‡ÃgΜ9ÓlyÚÚÚÚ::::ðëûûûû»»»»Œ½ÚVNÁ}©T*•Bˆžžž˲,ÆZ—Ìe|||œ÷;”ÊÈi4ëׯ_·;ç|þüùóüo~vYã匌ŒŒÀ:ÉçóyQyáp8ÌÂÙÛ®Ñh4vvvvT¾dà1¿F£Ñøýïÿ{þ,‹ êøjµZ]^^^fìåøÿþßÿûªßÍçóyÑ|É™™™™ …B!xV>ÛãÇó¿kµZ·]$‰twww;}<ŽaÎÈÈÈH£Ñh8)öo?ÚãÌ™3gxb!›<§\.—7666àkXÅëðììì¬Î¼¯(ñx<¾¹¹¹ 'jq…‡Ã7oÞ¼Ù,óp4Þ»wïL´A%s¨Õjµþþþ~>1¦R©”êN1ÇøÿOŸ>}Êÿ†`wwww_Çs…¾¾¾¾ÞÞÞ^ê½ááááf»D =T `û8êKÔÙàjµZíììì„É\šé ”²J¥R¹|ùòe(ã¥K—.ÁÉQEiƒçÖsß§ábÀ˜»³îõz½þüùóçN¿$p[Õëõúúúú:üŒÎ<÷ïß¿ßh4ëëëëG-¡ÍÖÖÖÏ£À)—Ëå'NœÀ¯‹ÀýÛö —k×®]ãÿ‹Å"V˜ggggáXÅb±ñññq(÷ÄÄÄüN¥R©¬®®®z‘UŠ“(klÆ€ŸUu!ØA™g¨#6v@³õLn| ˜£½Ì˜Ü ×*7€Ûnú-ÖT_õ]nzƒ}¥Yæj•Üå"]¿ÄeóºrêÓ•ØÜÌG4¦EmÂËQ ”Âå81§âßÕ+®‡<¸páÂüšÊïÚ¹pà¿-j/™IY5ð·™.?Ìó^Úƒ‚ª7Õµ¯·¢c£¸¿CDuF é¦ÅM4®è˜œ_ ÕÁE ü»Tc&ËJ \'ªœŠæþâ‹/¾€eµ"Îi/XTÛP}Iæ+„uÈë·Y‘X.j‚Å0ØM¢¸l>ÉxUxË•Ùnl~õÕW_‰Þ—m TêF&‡Hù£Ž5º=%¡S°óƒ¿óÎ;ï¨(~R‹–×y•Z×¼œZQ‘Yõù©¾MÉwp žk(àƒ>ø@õ”ÆŸ]ÐOR©T*ÓÓÓÓvp÷îÝ»üï®®®.¿'CîkÙØØØ(—ËeÙgq|÷»ôööööõõõù)§_d2™ÌÚÚÚô7Åb±Ø³gÏžÁ‰;—Ëåà÷‰DâÉ“'Oì:96ÿïîîîþçþçBSU+ÜÜýÒŒ1–Íf³Ü´÷çg³Ùl( Qf}Ü—’ÉdòÖ­[·{ÝçýëÓÓÓÓ•J¥Ò¬¾„M”ØT^(ü˜âcçÅþQ·>øçÏŸ?¯×ëuü:Ã!ŠÙ€±6Œ½\lnÞ¼yûW!ï¾û÷±Y•1÷®®.þ;²z‚õÉØË9úǹ\ºâT€}›"‹Å~ó›ßüFVÏœZ­V»~ýúõF£Ñ‡Ãáµµµµƒƒƒ>çÈÖ¾X‰”'ÓÀñzwnÇ?ÖÙÝÝÝÝ‘H$‚Cå»T,ÀØØØØ‡~ø¡[ß$‰|öÙgŸ‰Ú2‘H$FGGG_yÑN»ifP»ñ‚È4KÉWɺeg¦ž5(çÿí¢À©³ß;mk“”6Úê“7fCª/Á¡Ê„÷™EÇ.‚_Ö^²¸ìTU¦¨,Ù‘R;³ºÈ­@Í/°Ý¡ìxœÂúr{ZAÕU!³T6Óeè4É®o U—µ¶ˆRòЬ=nwèÍhëœ ë³l­qršƒ*ï Æ^Õnq A&“É<{öìŽ@N¥R©ÅÅÅEJ(¾c£" ívWŒý}ÚÙÙÙɃ=øNBe—²³³³CEŠò×eQãP£ƒ¿…ƒ{št£Àa`&ßÇb±ØÇü1ÿ<¯C5;444$[ àó5Æýû÷ï×jµ šiÕiØ|çÁ£naŸû R=ÙÁµ|/'TýF<߸qãÿ¿R©T.^¼xQ5JïÎñÜ xWÅs Gd9Á»s]mY–5???Ïkvvv~ŒAËdGGGG[[[|F^?ø´‚ì(#lQ $¶ AK ÖƒõÒŒ hæpë5G«çx|2ŽÏµµµµ þ™b±Xüú믿Æå%“Éä—_~ù%µsÅb±{÷îÝsZ_¸=¶···>|ø1÷í•)¼B«ÈÁýf‚²`œFþÃÓÔú ÇÄŒ½j&“€é¨ˆ"…¹)œwºp8.•J%xϳÈL “ì0x²T5óèN@°C\»vínàR©T⃱\.—“Ïr2XZá`ìGm{ooo›Èdf~;sÔy4úã ÆÔüAà‘VÆÜ›Ç)ø$ÃÝðøï‡Ø|ŠÇ”lÑÅJ>”¾‡] x€îø{p¬@‘—OÕ3>•ü»ÍrQ&ê™™™8/‹ÅâÌÌÌŒ×ßâ ÔÞÞÞ^GGG_„ìÆf</---A <ªÇØKÓõÔÔÔÿ_%&·l·Vµ>qù?999ÉûåVcâµ<p MLLLÀF;w7Z£TϘâcRŒÑƒ#:ÿ/² 48á߯ Üh4xw'VÑ‘9>9Q‰IÜ€û4_«ñŒ÷¿žþ'‘H$Þ{ï½÷øÿxG§nsY¿ÂÊ$‰PÊžhàxÚÝÝÝýãÿøG¾ÈœŽÔüÀ.€”£² rìLüüs*§qp½Á¹[7aλ͜_í‘N§Ó*îkª>dPVßÏý3ÂÀZ¯hÇÛL38l0YC•ËåòÕ«W¯2özéùóçÏÃÅ OÚÐ´ÌØ«“ Œ¬O$‰½½½½ƒƒæÊ¨x<ãÿ‡Ãá0N ë‹j3™ùŸÓJ7@¹\.G"‘Èüüü ª/ÁxëÖ­[ÐÿF£ŸþùçüyyëØ5{G]S>w ¼«….´ƒƒOR8û×ñ‹_ü‚ÿÆÎÎÎN¹\.sKÎ|ÖLw T¨¸«‚×4?ã÷Ï…Ô‚´¸¸¸û!>CekxG,cjjjJfâç îC©T*…Ç,vCA«kµZ­NNNNÂ2¸+À®MšÑ^N`°••±æÄ™ € Â!Y0cŒ­®®®ò´–<yhhh¨•»w/`×Lô ˜3ÕñçfLŸ—™øïܹsÇèp—1è³s‹Å"vär¹ ¾£Æa+Úã0¦²*P#™Ý›åÇãp_I8/---Åãñ8Ïi ;$ŒPýû¿ÿû¿‡A&ºs)7‹ùùùynnâh±°,ËúôÓO?åŸçÖ h¥ò'Ø™ÿ9ØtÚÌÓð<8÷ÿóÜåÐ,Èfh²æ“ô2F÷¥b±X¤r¢C’ÉdòÊ•+WÓŸ—[uª¥Ñh4.]ºt OÂØïI¥SeÌÝi6#ÿêW¿úßUó>ÅÙp8þÕ¯~õ+þYä':U ›^efXê$¯·­­­­b±X´,Ëâ}ÜîCª4c aåQ´ÈéR¡éš°áB.ÛùD—óSsð…²]-<^ÅØënXŸ^fEè¢à‹.< ÖÛÛÛ›L&“|ÐCE‡û©ÁMõ%êxoçÑÑÑQ®PÉú’ÎggŒ6Rí­=Õ€5`3òÏþóŸ3öê®.Hü}ƼAdÌÙó`Eettt”/ZOŸ>} ŸãwÞyÇݳ¹Ý€•GÑ"§K!„Š^˜ùü7”XA‰Åb±¹¹¹9øL>„å4«=T­mAæ Æ^$ðq3¨)C³¨(€ßÔjµÚ¹sçÎY–eq¿¥,ï7ÏaÆQÈ¢üE'šM±X,bE…?òÅ(ŸÏçñ R©T™ìp<…Ìï´¹¹¹ »Yn蟅íÂ'7ìoEûâÁMõ%Q Ò‹/^ð>o×—šrÅG©[sp€uá6»zîšq’Aùêùÿx÷%òßær¹~½Z­V·¶¶¶øÿï½÷Þ{|ìãLq'Nœ8ãc(… û±©[ì8xlú@÷år¹t‰µb3ÇOª?¤R©”jl<òÆiV{àùä0^÷c¯ja° _´™Ø˜ñÝŸ¤`PЗÍf³vgQá„59Þx˜ˆÇãqî÷æ;z~ê…²TðÓ²#ZØté÷QFj×,º«ƒ¢\.—ß|óÍ7ñ‘?ë|ðA?5ˆNJ0FûÆ¡{2S&^*Óž•»q|ƒˆN¸T*•Ê[o½õ–ÎyÒâ>*‹Eâß¡¬ÍŒ½šŒ ¦ nV{¸ùÀãöyƒÁ`0Ž Øjpd¬Ã”éOtU"7“VóªÁ`0 "¢Ñhtuuu.î”îHm–ú†ƒvG¼Á`0 ^QMðâÿ$vÁ7ªVƒÁ`0#* À‘Úýcì*àÈi>ñóV8ƒÁ`04»õïÈøþ z0 €Á`0DJÀQØüþ¤Õ ƒÁTøÅVËa0 ƒÁp8€G&àiÑëAó(ûwT2GyÅk–,x Øpù{Ä`0ÎaPdi2b„'ŽSй˜â²Ö¥ˆñþ}q&÷„Á`0œ +p·*’ï(î6ñB­kAŹë|—Zàe y ƒÁàAVà"hw‰ÂQÜqÂWGxUšx{`Y°Ká(DßäÈÜ@ƒÁà™'NœPX.^¼xñ(OBÑh4zñâÅ‹^Ê8}úôi]òŽ/øØÕaSD.Å£hIÝåýyuË׬ò¼¢’0J÷ •Ý5¶d;$*IŠW¹Ïœ9s†_µZ©T*ï¿ÿþûø^No0™™™ !øÕ¸Íº>›‚·ÿÚÚڼʖ‡ÃkkkkA]¼½’Éd2©T*ÅÿÏf³Ùb±Xl¥LÇY:E'æ7 ?G¶àx‘’I6€¨INZºË úóê–¯åéÀiÆH¯;Ù® ×^TeŠU®Yq[Ù-FAT(à¸q#£W €ê¥6¢>9Œ¿ËÓÛ;#ÜL^pìd2™ n?ªípQ‹‹EUø»*mT€ês­r9©æ²wë·Søû­Zxa1‹ €«^¯×¿úꫯœt4jÁONM^åÃeˆ&/'æR•ÉÕíÄçÕ-ŸŸåé@ÅO)½NQž ^¶¨ÏØ)o~ES;Í#q˜]uä·UYe ìNú®ÎcâºN'©ÖãaÂS`¡P(Ø5j4vuuuñÿëõz}}}}~æÔ©S§d¿ÓÕÕÕƒVFãúõë×EANÐ!cŒÍÎÎÎ6ÿ?‹ÅÆÇÇÇáïààŸJ¥RY]]]¥Ê›ŸŸŸ·,Ëâÿãà¡h4†ßÙØØØ(—Ëe™œA}^ÝòùU^3I§Óé™™™øZ,‹ ª–êêõzýùóçÏyÙ¡P(‰D"TŸ©Õjµëׯ_‡mœH$££££Œ16>>>‹Åbü=˲¬ùùùyçOù#ñx<>000ÀÿúôéS/å1öR¡h…‚W,‹T`.—ËaѸ{÷î]ø2™L~ûí·ß&‰íäÉ“'½üF&“ÉÀ€CXQ~xŽ“¯C>‘œP9r³–íðUL´‡íyu˧³<êyìž×ip$µãòºëðšP‡{¸þD²;EuNˆ,°Þde9­_·íá4é–N¹¨÷àX¹WUû™J€¡Ó]¼S @3ÚñXXxÅÃã~Läààà`qqq‘¿¾°°° ÚyâÝûúúúz¹\.ooooó×::::ÚÚÚÚT…ÞÙÙÙѱû×-ŸhWìv7LYΜ9s†*Oe÷ÏX°ŸW·|ºÊ[YYYý‹Åb=zôÕW_}E½ŸJ¥R^MÒÕjµºµµµ_P1¥b뇛qƒÛ8‘H$¾øâ‹/`V*•Êôôô´“r)Ο?žÿ½½½½ýðáÇnÊÉf³ÙgÏž=ã >n¨ÏV«Õêòòò2|MV¿ƒƒƒƒÐòÁØë»i4K®B¡P€s}¥R©´···‡B¡P:N»•bY–õöÛo¿­2?¹%¨ít^QVVVVr¹\ÎI¥R©”Ïçó¢÷á fŒ±Ç?®ÕjµþšÌ¬‰'1Ýx•R.—Ëðµ±±±±?üðChZcìåÄJMÆpÇ '1»òR©TŠk¥²E'hÏë§|~”‡ ‡Ãáwß}÷]ÑûÐlî·S[[[[GGG‡—ß.—Ëå………ÙgTÛ–±—»|jG‡Çùúúúº%?‘H$ Û¤Ñh4FFFFdsÔýû÷ïËÜYÜŸ bÇãß|óÍ7ÔnujjjŠ èŠ‘¡K.ØhY–uîܹsº6WŒ½\:;;;«ÕjUW™"ü®¯£ÈŸœ$A˲¬k×®]½u£Ñhð‰û÷pƒÈ°ó ....B3hAôC>jWüñÇ ?㥳Qå©ôçÕ-Ÿîò`‚—žžžhaìåd …Bíííí•J¥B•Í͈”r—Ëår*J\$‰twwwÛÉÛÝÝ݉D"vŸ³[¢ ¥R©d—Lšö©y____ooo/ÿÿñãǽÊ]©T*o¾ùæ›v;χ>„!ƦNçÀÆ^WT •7V““““pAäJ?ާÒ)Vwwww÷÷÷÷eßqB6›Íê²"¨àw}U«Õjggg'ŒíhæóùÁŒÑ[Œ½:ááI1û‰jhÖÃÚZWWW5áéØÅø)†Úcœì˜tôçÕ-Ÿ®ò¸Yî")“#ß©×jµÚììì¬Yeìììì@YUÁî·AuÕjµ:999‰_o4 øœÉd2yëÖ­[øu<1Ã`E˲¬ÍÍÍM7²r²Ùl¶¿¿¿_¥ßáöbŒ±ÞÞÞÞ¾¾¾>ø67Æýû÷ïóÿùÁþ0¼íìììÀ (‡çS]r©’H$SSSSN¾ÇãOž4aqìÒZÂAÍØ«>ÈçÏŸ?¯×ëuþÕP~ã—|xW ±Û —Ëår$‰@-“R¾8|× M|A|^?åÓUÞG}ôUŸp— ­ §1.Adsss[Tüô"Ö76Eommm©˜Œãñx|lll ¾fY–ÕÓÓÓãtñÁÏGY)°•Hôüü„gddd¤Ñh4‰DboooJÅ›Íf³Ô‰ ra¨ø’\.—S èÌd2Ê’¥Š,´˜ˆ¬*TžŽúr›£C&—¼æÂ¡Nì½Á}ÌjR…B¡ Š Zàc$p²¿¿¿¿»»»Ëÿ‡Ãa?}ýÍ”O¶+v³û§4[Æì]0˜ ?¯nùt—‡ øG¡j²×Îãssssx¢Wo¸{÷îÝR©T‚nXßØÊ§ó Z|Üš±)‹´RPfc•8žƒƒwøð3Ùl6 •‘¢S. JyÏår9»ãÝ¢ÀØVãw}5Þ`ìõ‰©ÀÅ¿Ñh4þýßÿýßágE“UÑpÇT«Õjø<6å‡Åµ xpQ;b]ò‰ –ÊnX”òX…þìÙ³gø³¢ì‚A|^¿äÓYoYŒ Þ¥ˆ&Gn&¦b Ë öY¬ 7›d2™¢Þ»qãÆ » Åbá  IDAT±˜N§ÓxÇÉëšde–Æ~]åÅãñøÒÒÒR+`Œj¹.&&&&DÏï4¹ ´ÂðúvrürEê÷ah>ÆÊ!妀߷[ð±™Yfv÷*—ÜíˆcÂápxiii Ž ¼€–J¥Ò›o¾ù¦(H´ø]_G ¡0==='ç8±ÿUÊš€léB—|‡… ?¯nùt•GMx|RÇYúü‰%‚e7‘õÔé ?ýéO‚ÿ©*Špw‹Åb™L&-5vJc>ŸÏózÏf³Ym@¹Ú†‡‡‡ßyçw°5‰rSÔjµZ¿Ý?FïÝ»wOÕwîU.UD™'¡‚W,‹°þ½DÁSQõÔØâG9©ÏQ¿ïµ¾dr©âÇé[âê8挽>1Äb±Ø… .0öúâ/sp¨4µ*Èv@¼FUû-Ÿ¨ ÀPˆ¤î…^wyùyÓ/ŸÎò`—Å\È&)Qº](WF³v-Ôé R©Tú›¿ù›¿;¾X,››››S)ïÎ~ýë_ÿšÿU¢±óù|>rfFW‘%‰Ä§Ÿ~ú)\¬½žwcÅh†\Œ½Üøáy+’~Ö¿.šU_‡7£#|{}ñ§¢n© /G÷(ó>ÖÒ¼žÐ-_Ð úóê–OgyðY€™_A¬8ç>cÍËX†*L RIõŠ-z?ÿùÏÎÿn¶kƒ‚ŠŒÇJ¡W^&“Éär¹œeYÖoûÛßE.eqõºáj~Ô—ŽôÁ<¸Rt%·ÝUݺyƒ1:r’±WÓËŠÌVT%R====Ôî(›Ífáw)?,>‡Ã7oÞ¼é¶btËt‚þ¼ºåÓYž“³ó"SýgŸ}ö™Ó¾ÊƒÝpœ‚Û %»à: ¥ìÔ߫«««p§èdLŠ˜ øceÇZ½œ‡9&'''¿ûî»ïZ)W&“ÉàŒrùØ¥¦”† Ì“~µãQâÏ1”¦—ÝÛÛÛÃf+Ñ `ØM Û9áÅJËJ3ãgj©`.ì…ƒltËt‚þ¼ºåÓYðص@¦R©ìgTÄ´ ˜ oŒ1¶¼¼¼ÜŒEÒî¶?ÊϪz,PdqÔaÙ€ó‚ 'Aw·wD£ÑèÍ›7o†ÃáðÌÌÌŒó¹rÁ9þà€>½¢¢ã¶sâò ?êë¨ñc/'ª7nÜpúeœÒ’1Úÿ*3µPD_–isNðK¾ ôçÕ-ŸîòðbÅ•KQ¸ˆ{I’Bawï§Avb^O&“É+W®\¯QŠG±X,âÈñ‰‰‰ ;+u´7HDZ¨#¢·gÆïܹs'‘H$œ´a3ä²CõšgJ©ÃÊE3LÚVÔ×aã Æè$vd³Ù,¥ÅRYeÑÇ”ùˆÊß,:¯ê¿ä *A^Ýòé.O”—cY–õþûï¿ï÷1(¯×Näàc²Ûþ¨ÛíÒÉR³î<ô^¡î?p«¤p³ºÓÄ]~ËeG£Ñh\ºté’ŠÅÉnœ´ŠfÖ×aä èó¾RìåðÈs‘ ‹Ê*hç{Äæ#QêT!ÏSkª<`3å "A^Ýòùñ¼Åb±Hõ¹J¥Ryë­·ÞZ[[[å€÷rÃNMÆ8Rõ*`Ê,Û-Q·^¹råŠ]@ NÖÒÊT™ÛnQ‰» ë2—ËåTî½×)—¨_3öržwzz¥X,eiËÀOx=E£³¾Žâe@ìoÿöoÿöÁƒT:ŸÁ`8àhâL&“iµLĹ‡Šøv[‡…B¡`“ Z:å:˜ú2 GxÝ.nƒ €]^wÃëàEÛß5ü Õ xš!×QÂÔ—Á`8²ðã‚|‚ã‹=œøü¸™ì(CÝÇ¡º@;Á©Ð,¹Ž ¦¾ Ñ›8ëõzýÂ… ¸R`vÿöØ]ÿêWÚ)­’ë°bêË`0;D÷„Ñ·Dd G+ë0¨rS_ƒáX‚•cöWG´p´Ú_T¹‚Š©/ƒÁ`0 ƒÁ`0 ƒáHÀƒ§¨ hl¶œ:ï”À#*"Úî<4~ž ¬+7r`%À,h¯*›¼>d A«å5 †#eÖnö„ GÊDNeék&º”¸à™EíÇvÇuỗ!`Pæc·‡Î² öœ8qK9ýâÅ‹›í 8}úôiÝeF£ÑèÅ‹/ê.×`0Ž ²ÄNmeepœ€×±Áàh¥¹T­.2w J &Ôœdg-Ô5½á$:˜Zd`Ç´KÅèDXª,Y¥¸©ê7Z18J¥R) …¨ëd[YÖa HŠ— Û\.‹:÷K?ž‹šwZ,) Þ¤ú·S³/¢) x•s*•JÁ ÄáááaÆ{úôéS¿d8 ¸‰Õ Ú]„ÓÓ2wîܹ“H$ðµ\.—kÊü¥ªP ,®4' G´à:]Ðu( *ßó8Ñzm|eÕ¾†û¨Ù"ÂÑq„Nlè—<~=—hì5SPy¶ƒƒ×û†h! æ U@‡À%Ú£²ˆ‹ÚȉÀQ²«¢eß×fPý ¥¥,,,,”Ëå²Ó…¤R©%x­V«­¯¯¯Ã׺ºººDDwwww$‰ðÿFãþýû÷E¿;555…Ÿ‡Ó4íËàŠL&“yöìÙ³X,³ûl8ß¼yó¦_ K&“É,...Ú}.‹Å6777½(áp8ÌcÛÛÛÛ>|è—<~>—lì5ÕgsÂðððp+z¶µµµutttÀy/ŸÏç{zzz,˲øç®^½zÕëœ}‰Çãñ¥¥¥%>~D$‰Ä;wîèøM»±‘L&“W®\¹ÂØËõjddd$ …ffffø÷ïÝ»wÏ×~¥b ´ejÑvc88k¥øwìzdÚ“Šöß MÙXìq£‰Ër¸·è´x9}nªMuÉãçsÉÊn†ÀiÿQµP󅹋>8ØÆàñåfÞáØÝôhwzˆ[M³$“Éä­[·nÁ×*•JåòåË—U~`fff&hooo¯T*ø™p8>sæÌüÝÍÍÍM¨½†ÃápWWWõ;çÏŸ?ÿßÝÝÝÝßßßWy.#ô›‡ÃáðÒÒÒ’É|,à.˜µgÞùøÁøøø8¶Bð ‘ CCCCnË3gΜáÏ]©T*««««~ÉãçsÍÍÍÍ©Xnìp ÇÝ,˲zzzzB'q4¢9ÌÐzâñx|```¾V©T*íííí¡P(4222Òh4ðý‰‰‰ ;e-›ÍfqŸá»wÈÉ“'Oâ×~ò“Ÿüä­·Þz+ …:;;;«Õj¾_,‹¼Ì\.—óKq”*Ñh4zóæÍ›pÀX–e;wî\­V«¹ùÁZ­V;wîÜ9H§ÓiÕò[íhÉd2ùÍ7ß|£{£´ò:Ê m£Ñh\¿~ý:Cår¹¼°°°¿ÓÛÛÛÛ×××çô·òù|+Ž”ëú›o¾ùFu uòY§HÊï?999I 'P ;E­V«íìììÀ×(Eû¿àk?~ ÿF£Ñ{÷îÝãï‹Åb~.N§á¢‘J¥RЄƒM/…B¡ ÎÂf£Ãtü† N¡L˜ªQÑv&8•]Ü3öú@¦Èçóùý×ýW•gvr*¤¯¯¯¯···¾F-Ìår¹¼±±±_£ú° 8‰vÿºäñë¹âñxüÆ7øÿ ÿû¿ÿû¿¢Ïû›þã·‹G4Fœºðt•£òkkkkv~u·¤R©”S î—TüÌýû÷ïc…Vdm¶Ãé) Ýíåd*øH c/MåxÁô›»wïÞ…ÿ `mZ–emnnnòÿãñxüÉ“'O¸2Óh4gÏž=+ †Éçóùl6›åÿçr¹œ¨ö³Ÿý ZÂápøË/¿üò³Ï>ûìË/¿üQÀc¡4ÙD"‘…¯Q&U?¨©(óÅ_|a7É'“Éä£Gaó4ç'* xÐ[7W¶ñ|ËY\\\TYu•#*û¢ßðƒp8^[[[“-d"(ëðÎÎÎC>|¸½½½ _£¬Í*à~Fýcþ´—(@:•J¥¨XRxï½÷ÞãщœR©Tâ¦r¯P;v‘Ö´³³³5³H$éîîÁš=öÿss0÷©D"‘ˆÝ}0¡P($2^¸pá®ìp8þ—ù—¡4c'“«¹víÚ5쪾1ʤꗒHõÊÍㆮ®®.Y„0u’Oõz½þüùóçÔ÷±5ŠêÃ"Tvÿ:åñã¹p?ÑaEtŠÌMHíÀœþÃþð>Nœº …B!—Ëåì>ÇAÑ®PW9Nvû²cmv¨,vN­NÖŒSKc¯º¹8x˘?íÕÕÕÕ­lêT©üÃ?üÃ?à†Ö•<›â“ÙÚešQÕ¸‚œ$±ùvœÅÅÅEþ:6½óïANgYÊ×›H$SSSST|H¥R©LOOOSÏN§ÓP¡‚°•‡±× ·$‰„]`nÑû¢ S/àÝ¿…G—<^ËÁý¤T*•œ(ˆ"ó(œ<áÃ'Nj!øÙÏ~ö³/^¼ v`¹\.§šãàûï¿ÿž»4¸2™LFe€Ü¸qã1®£\&5—@xð$Äæ>_ˆbx¼X?~üšÉ …Br7ËŽ3öê¼ÊÁÇK©þîG{1ænSÎ0666æfçšËår°‚ööööœä îR(ÍžÒ¸ü‚ÇP§øéØ‰Ýø–‰Dbooo×!×Äì$Ý–EÈŒ}øá‡ÂvÔíOuƒìx§Ý$OPàpwçdw€­XªÀÝ¿Ù\—<ºŸ žù·,˺víÚ5Õòý†²ÞATÏaïíííñœ%ªn€h4˜˜˜À¯ÃSSÔœ‹ÅbããããºËáeñø&Qž8NüXø1ù|>/zŽÌ*@m{ul]¹råŠG¾³Ùl[Žu¶…ÓyLYPÀ)0 _¶>ÀI kö2Ÿ¥p)•¸ˆïPq€ W`Êår9‰DðQêX &›Íf¡+CgY˜éééiØ!c±Xìã?þ~FGR¨V‚ã._¾|ÂŽŽŽŽ¶¶¶6¯¿¥¢☇åååe¿&[/O²r`¢Æü}¿ â^(àWqPÁ–ØÍ*:1Ë×UÎÊÊÊ µ9ãðJÅ}êµZ­Ößßß ‰ør«€Ž¬›: Žêj/Nç1e€1÷V Q>^@¡}pppjñº{T™H("Ø|OùÚð™SQ½é,‹S«Õjׯ_¿.R$d¦ÿÃeìå3ÏÎÎÎÂ×ÜX\(TúÆèèè(Ü9û4§KiÆåÀÑ ö˜ß@´cÂ9F( «RÅ @[âþÆØK7Üòòò2| NàºÊ¡€u£ÿÅwìnPU.`L—SW"g~~~ž/°~m^‰DâÛo¿ýZül/7ó˜P˜™™™Á»Gçâ$Úyþüùóz½^çÿË‚–‚ìÿÇðÅZÇ"­³,Šr¹\¾zõêUüº×¼A`kkk‹Ú™b7Üá:‰‰ÁƒÞl&TÙ9ë’GW9øÌ¿[÷Žá@·•J¥¯S ͨ“/˜±WS—«¸‚´y˜°sCᵃ•·ÁíT" lÀ뜟íåf#®­Sg}f2ƒ¾ ¬]«ºªÕjukkk‹zì³Ewúj*÷TI¹òTë»tõ‡VÁ}ë~´‡à\è&j297›ÉjµZ„J@,‹ :)§Y ¹Ÿµ,Ëúíoû[7eäóù*‚R´¨9ç‚Àòè*ÃÝi2«·ú¡ðݾè¼<Œû°»¯÷Kjã@¥‰vk1¢rNpüj/ÆÜÍcJ µh§R©”Û]Ÿ+6«D"‘¨AŸ€œo.œœœœüî»ï¾ÓY>ì¨"íçpgìeÔì/ùË_b?—ß'Qý0‹Å=zôÈë9Þ[·nÝ‚eP8)‹¾Kœº|J¶«‡»Qt°]òè*G%»á mvª°|Õèq¼‰ î~§ÚÝI&JRýuïåúƒòè*G´ 4;®†B¶Û#þ{=³'>®Ê˜ûÓd¢ºæk“ßíåt#/s¡vês8«u å[¦î2VÑ"íüKÍTÁ²Ã:]QŠëN%Í(üþyX¾“T¥"`–5Qy8l'*ñŽjà益öt{/7ODõ=UpÉî¶·“î®ÝÜ¡KÝÏ¥ò¼ôeSNïqÇàþè´<Ü/UæKÑ<…ŸO–¸J„ÝÝñ^ÊqÛv~Z¼_„ËS·©Ûy‡’_G{i›ÇTQ%ÂϪ*¢J°[Ôd׬äÍRx]ÉU§ ²ØP™ìfDª¼tT‘lnLxðàÁ»ZœœÊ"룢%¢K]åÈh…àT¹ÁãLe¾-Ôó9 š“õ ]å8å0\̘³v§~׋@•çµ½´ÍcN;+€À@Ö¢ Z7ÍPøä‹ëC— ²ø«X|T>ç‡ ú]•2(yVVVVdåÙõK•ÅÒ® ¯»Ýòè,GD+ÆÔjŒ©Ì—¢>/š£T•E[W9G•9CÔFnٺ䥽Dó˜ãM²€1z@òâD`Œž`d“Ÿl1ÑaW¡ €ªvèt0cÙEu-š X†t˜Üe2ÚÉzp î~â¿á´ÿªÔƒJáþ¡Ë•åV¿Ê±+·Y G4YÊ&mÕù’Ãv›]÷Âë*ç¨Bµ]vª8QâÝ´—ÓyL§5Åà3~(v‰„Ž ²Ó*àÐ,K–Á`0n]~í0#ASLÛ †Váè2 ÃáƒÊàGÝcmŸÖ€oqtsOƒÁ`0¸Á(ƒÁ`0 †£MÐ\ƒÁÐ*ŒÀ`0 ƒÁ`0 ƒÁ`0 ƒÁ`0QB.Ñë­‹Ó¬æv8M§\/­®“ öY®.—›X&àœ½É¡3 "øµÞøõX,»wïÞ½V&·;ò[›u$5ŸÏç{zzzø}͸Yô0Q( ð:öD"‘øöÛo¿M&“Ix}µ›ÛpàE„±—gëM4¹Á`ÀÀk½á=ö3333ŒÑ×7“¹¹¹9~õl©T*õ÷÷÷«^¬‹jµZäJ¾"÷¸ø™™™ÞoÂápxmmmmooo/‘H$d׌4Í@|×O½f0ZCL¸0·ÝU¾­Xð`aé¶ê⢠ö»k©}]w¼^’rT€X?­D¸C´Ú¦êÌ~Ðú¾PFebðë¹TǩɅÐ}út3Ï Ñh4zñâÅ‹­–ãØcüÝ/,œXEŠÝ¦èÆ=·7Ráïy 2ñ‚›[»š ^pU•.¿žËé85ãZNv‚CKÀ;œã/6ÓÅê§ïôŽÓÓÓÓ|Âæ·êñã=¥R©ÔL ¶Q£Ñh\ºtéRµZ­–ËåòÕ«W¯2f¤¦­­­­£££.ðøXc/«r¹\öÿ©å_ÌjµZíܹsç¸LvŠŸ@¥ Ö»Ý÷üx.ãôòåË—yŸN$‰©©©)•ïÍÎÎÎrE3‘H$FGGGü®Á¢#…ïd2™Ìâââ¢Ýçb±XlsssÓï9¨eò¸½{þ¸ÿ©ÝéƒÃx:ÁÎÍx­p¸Röë¹tS·Ï%‹i&Í0¹A'&xh0.ûÏRA´vˆâ¥Üǰy\äèPÃKœ´ÑaiO'QÓN>«‹w~Àé÷üz.?Ú5FOœ8qBå³8¦¡UqÇE >+:^臠 ŽIÁrQ oQ¬GµÏã £ÓX1Ýòp„.€¯¿þúkÕB ­ÁI–öüæ›o¾QMBâ䳺øÿ~Àé÷üz.?ÚµV«Õ¾ùæ›oT> cÂápxbbbB·<†9sæÌîîáLNNNbT±X,¶"¡c/¦7nÜ€¯9q•µ Å©T*•Ë—/_æÿW«Õê¥K—.q·c/ûü™3gÎVyþ¬P†^4 Y™2mRvF™ÒxÜšK!ªÁ2”馎t•„çÂm,ûMm]g?ô˲¬ÍÍÍÍVË™9 ÍŠ!rC____ooo/|mvvv+P8ø•1ÆNžQRQ‹Œ½ žs:€2™LæÙ³gÏpÇJ¥R)Õ£¢À™D"‘ØÛÛÛ)\)Y[[[ÃÚ7Ä."SW9Az.|ê¡····¯¯¯*§««« þŽ_“×Q¬ç –ÃyøðáÃííímþcð<¹ïîîîîïïï·JL&“Éày¿R©T¦§§§[%“ xnj4ê³8çþÀÀÀ€î`@]òP›f8žµ+…B¡ ¡Ê'ßRWWW6)á²nÞ¼y“Ú…©ÊÃØKeBG…® >dåå¹6777ái™ êüùóçáÿ[[[[A0 †zr9µZ­6;;;Ëÿ?*Vƒ3D¦ÿëׯ_o…+ §N:ÿ¯×ëõçÏŸ?§>ûøñãÇðÿH$éîîî>ŒòüYH§Óé ›Íf íæxŠÊ±§D"‘À;ê3øø’yìÎbÃË< üb(ìX”—r‚ô\Õjµºµµµ?C™ âñx|```¾FåLÐÑu<cÁªç —³ºººÊúåm&ºûáqà0šþEͲâ—<Ú,Ñh4J™þxr™P(joooç“'‹ÅÆÇÇÇíÊÇO^æ»yB¡PhdddQ0F'5±,Ëêééé‰D"ªCçóù<– ïxu”Äç 9eëîîîŽD"ø»~ú.b=±¶Œö»( êVÓ?ljß|gggùÃ*6€ Z(•J%˜”'=áÀ…[ND“—p::::ÚÚÚÚDòPIR`Òöc×jµÚèè訹ûbººººàsé('ˆÏ…Ý” G/û©aÕzZ9hPUì9T€¥]^ƒ\.—;J HÄ‹,?ùx›Áa6ýë ‡»ºººZ-lj<Ú*hî8Õjµº¼¼¼ _ƒ 7µ[Ä;Æ^]x¨€³ùùùyíà³û IDATª|ÊMU ] Us±—r‚ø\Ø €MÀÔ‘–õõõu¿'‡£VÏA-‡ãÆ ÀO"P¿•Ëår¯ŸôáM&^08J¦7ÈôZy´)~-ˆ‚ŰéN¼XžX,{öìÙ3j¢£NÀïó‰R–{[åñ¹{Ý ­:<Ý0Ïï{Žj=­ §V€‰‰‰ ÙIÆ^ÆUØÕ·¡5P¦lù= `k— ¼9²<ÕjµÚÙÙÙ Ý–|=õåàQãÎ;wtL”ºÊÑ…Nyð.Zuᄽ½½½ýðáÇ:~—â¨ÖsÐÊ¡€f1¦ €c+¨x!Œ®S$AßÁÁ€D—Q©T*íííí¢ Þy8”éß²,ëÚµk×tý¶yt–cg•n6~Éc’Édrhhh¾¦9íG9ºÐ-vÄb±Øààà c¯ùiþ?ªõ´rdÌÏÏÏseÐÎ À0¼c¬Õjµþþþ~jÑãŒét:íF>ƒ(Ó?••ð0àÄ*íÄÚty´)~ ,J´ K–€åᓞäDð ¯5ÆÙ³gÏRþ-Y䦮r‚ö\l¶:þüùh4…þp¿ÍÿGµžƒVŽ ç# òýè£>²Sñ‘“fÉ£M ¦ŽQÅãñøØØØ|Í."î&9Ô1-¨H`y¨2Ü RVðBçW9A~®û÷ï߇²uuuu%“É$Œ¦÷Ûü9ŠõÄr0Ð ÐŒ«‚÷÷÷÷wwwwák¢c‹øu?vpGY¦]òè*f³äPÇy) š½*ºäiZ&@JàT*•‚Gy¢ÑhôÞ½{÷°ÙHÅ$|ëÖ­[0»åÇ„Š%Ïâââ¢,S¯,ÕÜîx×»ìÊ òsaÙz{{{ÿú¯ÿú¯¡ÖÝŒès=†r°€š¼tR«ÕjØŸKef\YYYÁ;W?vpGY¦]òè,Æ®0öRq½sçÎþ</---a š͖˃¯sTÉÝO]i>³jw4I>7ìFžƒû{´ñ¯rIŒ®r‚ö\Ù}Øn.PqÚj=­ðîC÷•»·í¥"W³æC,Ý…M2°Œ­–çààÕ#ºÚKW9ÔnÙÕÔßn®Ö!ÀÓ‚Ký lÀPƒˆ’çÁƒì*B499‘‡*ËKýøQNО #› dßÓÕj=­U`ý¹ù¾Sìnñ³{&Ýó¡Wyt*­–çà@ýè.lj2á$á’@‡î5&A§ ’Bõo?Æ^ï^••1"zF Õ £ºã¾ã—àåYTQ½¾™kNêHNëÙXM^%©€£Ñhôw¿ûÝï‰DBg¹år¹|öìÙ³F£Ác·nݺ¥Ú!3™LfqqqÑîs±X,¶¹¹¹)šTu”£«~üªgÆJ¤~,‹ òÿ£ÑhtxxxXõû|BÌår9ªìgÏž=kÅÉ„“'OžÄ¯Y–eõôôô„B¡P±X,6K<¶r¹\Îíd«:FTYYYYI¥R)Æ«T*•÷ßÿýZ­VS‘ãÙ³gÏb±XÌî³øšãýýýýÝÝÝ]ïÒ¿ÎåË—/W*• cŒ%‰ÄÔÔÔ”Îò'&&&Âáp˜1ƶ···>|øÖ)E6›Í†B¡P{{{;—5,...ÙMe`êÚ?ÄI£:M&ãF›§äÑUŽ®úÑQŽÈ@iû­²À÷eí@É,z>Šf'J![xý²p`ݺÙñ9©o»geÌ}î§rPíËÐY×p¼èÜY«XIUæ þ=¿ä丵´)öãâÅ‹ƒ²Y Ô$NuDØYÝju'Nœ8aWù”©þÕ U@·åèªåÈ&J<Èd €*Ôä'V‘K4áºv£”ø9±àºæÏädQ÷[€D£Ñè‰'N8ùŽJ l;àôéÓ§ÝÈ®ŸÇ,ÕŸq9~ì>ÝÔ³8FEãSÅôßJAU·`+àc:(ò´7‘ÅÉd2ùÿñÿÑ,y¨A-„°#úYŽ›úÑUŽLÀÏçUPQà`¯×ëu˲,<à¹ð}ª,JQ€2SÏî÷@. jÑS]hš©8…ªOYŸÌü@3ä°ë«…B¡@}÷Ù Ÿ¦Â²Ru«ªI`ŒÞ|µ:@g`:§P(\i;­®Y—jP]¦UQД¬|'yd“°®rtÕ®rd î3^ªÊ´ýz½^ÿüóÏ?‡õ '¤<øŸÿùŸÿ¡ä¥&:»» D2aÜôC;¨ötè$°"šCT'Z\/^ç·ò¸•CdÊÅu1?«ìþUMîv ®c•±egáPý”ú\+cÜþR¨Ô¥ˆÀRÁ3‰D"qçÎ;nËäDqñò÷ööö¨ÑÕÕÕÅbc¬Ñh4vvvv¨rž>}úþ?000ÀBW9ºêÇzæÜ¿ÿ>c/£Îœ9sÆkyŒ½œ pÐQ¥R©LOOO˾÷âÅ‹<0íÔ©S§ÚÚÚÚ::::clgggçûï¿ÿžú^____ooo/|myyy¹Z­Vák««««8ȉ ÎcÌ[?ÄàEš VsäʉÇãñ¥¥¥%ÜWGFFFòù|^ö\¢ °ÅÅÅE• ×›(Í/òP¢ªr|ñÅ_PÁ…Åb±X*•JüÿD"‘U{šæÇãccccüÿÙÙÙYêYü nô“jµZÝÚÚÚ‚¯ÁyµÙܹsç¾n4ëׯ_W LåÀE_4Ÿ¨8 V«ÕÖ×××ñë©T*åT»aìeE©VP*•JáÉ÷Ô©S§àÿõz½þüùóçÔ÷?~üþ‰D"ÝÝÝÝ:ËÑU?ºëò‡?üá–eYŒé9 Çã7nܸ_S4ß}÷Ýwõz½Îe9{öìY¾PbE B)l\±ÔjµV人ººð3{퇾“Y[[[ƒòQ„ÃáðÄÄÄ„Êoò²7777¡"Ñh4gÏž=[.—ËÔwTŸ+‡×ÖÖÖðîî„ð‚Í"¼»”)^å "g}}}ÝÉäL1;;;ËQÆ»qãÆ ¸^cl|||œ·{¥R©¬®®®RŸ£úûaáîÝ»wí>ãÆUËårv1HØ×y_´›su-ú?+ét:d³Ù¬“‚ìÎ$ËÀz~~~ž/ ~ìêàÀÞl’Éd2N+*•J¥d ¾»»»»¿¿¿ï¤LÝåèª]å`¾ÿþûïù.¡····¯¯¯Oõ»ssssxw»°°° Z” PóïèèèøË¿üË¿dL¼ ‹)lX‘èèèèhkkkãÿëê‡ÔmÇÐÐЊ FïÝ»wÏÉâïæ¹ðÂGYZìØØØØ dÒ!Owwww$‰ÀÏ`…Ü år¹¼°°°ÀÿÅb±¹¹¹9Õï{Ÿeàݿƒ•™Ã ÜXÉŽú....¼îZàʸ®Å˜*v¿S*•J¡P(ÔÙÙÙÉ-•”ë*³0örÒ-N¾C |>Ñh4Jí~ffffà@Áybbb‚/x"s.ÅÎÎÎŽh`è*‡1=õ£³ÌÞÞÞ·.xu¸5ýCø‹Åb.\¸À˜|Agìu‹ ™ÅFW?dìÕ]š**õ¾žÂnñWy.ê x,‹Ãg´³d`¨œ.yTƒ“¹o¦§§§áï«*g~û•eYÖüüü¼ìóX™9,ÈæU•~˜Ëår°­UsD¸/ü²\ Ü5Çûw:N;ý@*Œ½\œ:;;;U4Ý\.—˵³˜™™™Á~Ìr¹\¾zõêUøšŽ]+c/'ß®®®.?ÊñZ?ºËÁÜ¿ÿ>lnÝ^Lÿj§Ë’c‡®~ˆwižpE¶3´«ÿ………'‹¿è¹J¥R >W­V«;wîV0¡<|gK)@øÙ8T‚#]òøI­V«ÍÎÎÎòÿqò V€c¨8Š|>ŸŸ™™™ñWºæƒs•DF[[[[*u¦ƒJ¥Riooo…B¡H$Q±‚ʬÀ)‹E•†¸råʨMc®L³ÝÜÜÜ„“‚®…[è§«·õãW9œ‡>ÜÞÞÞf̽BåÅôÁí˘¿® ºúáààà ®‹™™™¼‹Å"ž˜±K‹Åb8äÕ«W¯ÚÕ1#7NµZ­.///«Êã– É#"h££££¼ýUvÿ|>Ÿ)o‡ ¾°bżV«ÕúûûûC¡P¶c?fÝ„;oìª)0c'šî›…6€wUœú°`Côôôôà úè#» ^Ü,Ø8êÿÒ¥K—ÜÔ5 ŒÕqô3ð ‘¾Ö®Ý‚Ÿ»]õãµìP}^ÊôoY–uíÚµkª²Cð‰Ë öÊ ¼ø4˽ ÂkКÓ5/8 l†ÕæùóçÏù©n_n-s»ûÅGÒð]ÍÂËîÿ(@)‰^OP …Ų̈Xظ²!Sªøé.§›,¤‡R`¬y¦<'»Ù.DW9ªèª/å`7€j $eúŸœœœôbƒ*ß^dn!ü\;;;;Íp/ˆpÀÈØËÉÿŸþéŸþ .dv§at¡جвéŽH&“É¡¡¡!þ¿ìØ_èÚý&Ο?^ôž®SÔ1@\&¶lq¨c€pÞ²³°ð EXñY.§D£ÑèçŸþ¹×‡\Êç"ƒ›šñNÐÉBw‚^ÊA&ºêGW92°àÝwß}×î;"Ó3o¶cŒNBM ñx<>000ÀÿǾg]ýЉ)ë‡˲¬ÁÁÁÁÛ·oßÞØØØ€ïÉLÓºZhVÅ>VðdgnÕ%•CWP0c/ÇÝÍ›7oB¥G”tÇOàîß鑨猽:¯í…e@•–)ÐŒ†wdýýýýß~ûí·2ÿ•±K¶àº5³Á],Ïbìu­ž±WM–^ÊÁñ:êGg92 Àݦ¯àóÎÔ±-|4gÓÕ)’ŠÅ &:U‹öMË\NäÁ§Dý÷s»9x€NypŸ ‡Ãᥥ¥%~z¸ð2ŒÝ¿(¯B3¡6§–,ÔZê8iÐOpüP^A%³P9ØeÙà ð ®Êg0ܤcw‰ .Gå’]åøY?nʱ»ã@TÆÁÁëA]ª·ÊÀwØõY»ÏËî/ÀÏN}_W?•£rÍ1¬C»»Tóà«È£òê¹í>Ë¥rÎëGõ6@ÜdíŠßËœêe~†2ëœ×aßqzÀÁýE[°\ŽÝ]¢6Çýþ³Ï>ûÌΊûe²WÍH¡ãb.×—a‚¤¨‚;‡Ê½ÕxaR½BM:ÊÑU?ºÊQ¹äHåòÙ¥B*ðzÒ­àÏÀ罎ÑÕÝ”cWž,¨~!оw#“t§*È”/ò¸í2?›—Ëh¼Ìϰßê¼Ç«  ìnæÔƒ¯ífìÕ:v³ 7K‘xC´,...ÂâˆCN+oT‚P~bêL´ªÕjurrrRõó"³µ®rÜ Ëî¦'n€  ,÷À…IW?ÄgÈíp“4 '«aì¥)~jjjŠ’ÇÉs©Êçóy'Ïè—Çã####NË7h€›h(- jiFÃ2ü‡ò™ê¼ÔFåö3“Ù2Xd2™Œqån|O$Øv&›§OŸ>eŒ±H$éîîî†ïõõõõõöööÂÏÙM3N”ÙÕ­:‚MT!e¤Ý tz&·—¬f¢EBV¦êÂ"Š1‘Õ‡ÝU¼"`;é(CuA¥>Çû¯ì gˆ™¤ º‰F£Ñ‰‰‰‰X,—}6(c.(eè}·åû¦ð+•J¥¨÷ÃápxmmmM´ˆòKmÂápxiii‰O`ñx<¾´´´‡ÃF£qÿþýûºe‡“îââ⢗2öööö‰DBô¹D"‘ØÛÛÛ5 ohQ=2ÆX,‹={öìÙa™è …BAV/¹\.'SyÝ®­­­Á‹V8¼>Œuè%¼>LìMkðóø[«åãwlllì0Ì;†×Ñ®ðÉY¶`©P.—Ë Œý8<{öìY,‹1ÆØÂ•î6“ÉdœhDÉd2ùÕW_}ÕÊ#‰‰D"ñ»ßýîw^dˆÅb±ÍÍÍÍ ÆB¡PPñ†Ãáð—_~ù%^°¢Ñhôw¿ûÝïdJ'•J¥Œð#¢: ét:ŒŒŒŒ4F«å2¼ßý[–e}úé§ŸªX ÁD»pçÎ;*“³ ù|>ßÓÓÓcY–_o4‘‘‘‘|>ŸÇßá“ï¬eš6×Ì×ÖÖÖÞ}÷Ýw§¦¦¦tÈí–D"‘ð*C,‹ÍÍÍÍé’I'ñx<>666¦úùp8ž˜˜˜€¯MMMM9é_©T*e|Ç?BÕ©ÁÊwØwñná»ÿååååû·û·J¥R1V€Ã‰V “Éd¨ÿÌÌÌ ÔîÛÛÛÛ+•JE¥ÌjµZíììì„ßD"Õ‹nr¹\îààuýÊÊÊ ´&ˆà²»Ý‘pe%DP*•JøóÃÃÃÔ ›Íf©2ffffðgdƒ·×o­V«õ÷÷÷˾C=eYÖüüü<ÿppp×7”…êðY¢ÑhtxxxXV7”Âxþüù󢺵¤¿¿¿¿V«Õü*C•J¥ÒÞÞÞ3›Ífñ熆††‚h"|“@Ík¹\.wXÜnº€»ÿùùùùZ­V›Åb±ÁÁÁA•2‚2æ‚R†ø†+©výó•••] ÔD›Íf³x§Î?Ì|¼l¼˜|üñÇóÅ'õü=¾q9á‚GYœÈ!SVÒét/šmmmmüÿ|>Ÿ…B¡b±X¤ÊÈçóyJ‘h6ñx<>000_ÛÚÚÚªV«Uþÿ©S§NÁ÷K¥R Öo­V«;wî\Àa( þäÌÌÌÌÀº©V«ÕK—.]‚ýÊN!:Ê‹Å"VÂáp¸««««U2&(¥•+œÙl6‹Åb÷îÝ»w\2šò1¸¼¼¼ÌÇöêêêj¥R©LLLL—z8,ØÅÿ¼Áثڃ[–ZJ¥RI´p1öÒÏÿ_ÿõ_ÿ%z›Ý¸l*Ô|¦v„P>/‹¼îÞ½{7exe|||O”vrQ'8ö÷÷÷wwww©ÏwuuuÁ ?QèÇnoooóÿìNŽ";;;;*Š65®Ül¼Œ[]Ȟʼn››››|þà>§ñ÷°ëGŠS–Fn™ÄÝ]5111Q¯×ëвǭ‰D"1:::ÚJù…°ûÜÊÊÊŠ×~!ŠÿyÍîœ(”¦ìva™Ý¸l”Yó8ƒwÖ»»»»ûûûûÍú}Ê4oY–µ¹¹¹ _{üøñcøÿÉ“'Oâ²ð=êõz½þüùóç^eÄudx•L&“¡Nf$‰Ä“'Ož¨ÌA·:ž…]x£•U‘ëÎnß¾}›z†h4ýüóÏ?çrÄãñøíÛ·oSeÈÞ‘L&“CCCCp÷Ïáʱ¨Áƒ¡s¹\N¦¬¬¬¬¤R©Ôâââ¢%ÇÿHcœ(x‚¥âñx|sssSæ›ç‘§²r¸–/óó§R©T«ƒy°ÛÄéâMEÕÏÎÎÎÊüMÔîË/“2ÍË& þ*•JÁºF£Ñ{÷î݃íE•cp¶œ`’ÉdòÖ­[·DïÇb±Øo~ó›ßÈÊÐ5n½Çã7nܸ!z_åYÜÐÛÛÛÛ×××§³L ŸÓ¨]v<?yòäI?|}tttTdÅ‘½GAíþ9Õjµº¼¼¼l¬öà9[¤ðÅŸÿ/STã„îPÙÅ=*‹…›ë)T“›``R»Ÿ|òÉ'0õ™†EÕWÌi™* }¨coNé0ùÊΛ;I¤e¡ê‹8Ôçx½¸IäåpS3^’+‰Qm§r‡J> X÷:Æ­]ªŒ7Ï¢ W"ùtä€eà¾$º^]öÙ{ü7dÇjy™:ÆËQ.ƒ1ûS%vï«&¢ä…ýôÏ€r¹\ŽD"‘PÈ>J߯Ä3¢(o)NE½Cp,öÛqÒétÇÈ¢Åuñ×ìb&dX–eõôôô¤Óé´¹œæ# b?¶···>|øú|±X,Êâ28¥R©„#gŸ?þ¼^¯×áçr¹\vhª^ T$0•Èjcccƒ¦RíÇØ«'+ìL÷:Æ­t< F¦Lbür3%“Éä£GQÖ“L&“%Ä‚Äb±Ø£GQйì=7ÏÎÎΊ>íͰ†v¨pn À;Æ^Ž%7±jvñ?¤ ûj–ïŽ2%ãHñ|>Ÿ—M&Õjµ:999ÉM"²Jãþ½‘‘‘‘ßÿþ÷¿¿|ùòe-beê¶,˺víÚ5·eê<ÿï$ûaç‚ 1ÔQµjµZÝÚÚÚŸ]\\\䓳ÊÑÎãL£ÑhÀI¼»»»;‰DàgðÉŠb±X”9ãV:ž²²²²ÂW®`‡B¡ÐÝ»wï>xðàÁÙ³gÏúÝ×ð_©T*o½õÖ[ÕjµZ( PÁÃJsµZ­¾õÖ[oñÍ̾*{ËÀ}ÿPq1???_¯×ë&ׄ="%@×⯂ïw8Šò¦4Nž&XTN±X,:9wY.—Ëï½÷Þ{~žÓäP™ìFãÒ¥K—¼úºS©TJf~§r ˆ¬=ª‰=°ÕÄ.öceeeE5 5!ÍÎÎΚ qîh4³gÏž…“¸êÉ Ù˜Ó5n½¢ãY8…B¡À'b<>‹ÅbqgggÇo…feeeE´Àãq4333CYÿøf.4‹‹‹‹++++²÷`gΜ9Ø|÷ÏáV“kB J €x Ni† IDAT]üíâ„ ôA¸Í‡ïlBE€S¦àÃΔHMÊ"¨¤HÔï›7oÞ„&|þ=ʽÀ'<™©ã;ø>û¡Ea¥†7nÜ€ŠH¹\._½zõªL®í}†£’ÈÃN™T)£³³³ÓNÁ£49Iš%‚ï^©œ:Æ\PÆ­.9’ÉdòÊ•+WøÿÔΗ:ŠO¹x­l6›å <GT®L:Nù.в÷xöN•Ý?‡+]"+@³ÆÜa)C¤x]ü3™L¯ÝF£±³³³Ãÿÿ³€}]²\þ|B“8¡NdgÅEÀÅÓ«ŸÜ-Ø·ãdñA%zq‰;???ýòv>Í3gΜÁÚ¥èè'Ï _Ëf³Y*6*Ïx±X,ŠÆl6›ý«¿ú«¿Â;<Õ[#3T$p(¤¦|ØáfÌùQ†ìäÀýšêÓ:û C¹U z¡|þŒ‰OP¸‰ÿaì‡L€v$ÐFM(x@ø™|Ÿ? ø±øsx&.øšÓ€$§“3øU©T*««««Ôçq›É\xÂ¥òˆÒ‹Å"å“>.¶_èsA·vrÀþ†wL"Ü{/CcìUÓ|±X,b³½ÝBÝ ð"5Ù{\'&}'.ƒãŽhñç8Qì \sÒ¸£°ÛEà3ÞŒ9ªÇ“4L 9lZ©Ÿ‹¿jÑ”átr¦ÙõõõõfÄQØwpª“øqDǘ ʸÕ!G4Â4É"7B3’oåóù<´î¥R©?î•N§ÓÐm—ËårÔéêvVî2½ËpØçÆep\Eû‹Nxù-Ñšóš}'tT„¶ÝMlÉd2ùw÷wÇÿÇGD7—Q¦ç ÒŒÅŸZŒš¼©È|Ù®ybbbB%àJ„ÌBäå8&öß2&?–xÜ¡ÆßÁAdcÎqë&^@dz`D ;V°ýR~‹ÅbÞ™³ÊÆ~LÄcޏ¿ºX,eïaœöG"‘ˆÙýË‘õ“tú;²øÆ~PÜ.úÊWF™§¸æ‰ÝÔ ÇÙâTï“^•ä¢sÀ|ñæu-*ƒ¿ëTfÒtzöŸ1ÚÅ@e¶¢Tfx²J±ՅݱÄãŒ(·s™L&#sÍ·vŠ¡Žg©Õj5;k¶à[/uS.—Ëo¿ýöÛTî ¬ ˆ°,Ëzûí·ß¦æÙ{»À>ÆÌî_•sþN•×ñ?n3g¨e´Oäº3йÁIVDÑó¸Éupðj–6·rÀvq›qNVŸvÙ¥D¸i[Y3tdòÂåà2dG/E¨^HC}ÎI&@‚˜ P$—]?Óñ,°}©9K;™¨6t“D—C}—ÊÂ'ÊÌg÷žˆ•••™Ü¼àúðcÌæ2x]ò÷TǬW]c_{€Ë—/_örl‰ŠFÇ4F¢‰ƒÊää䤗¨ïJ¥R™žžž¦Þ£¢ùU ¦§§§ö /wX–e;wîœÙý˹víÚ5»1gw´Òq+²(âÉN :žÕRqÐý… QÔjµÚúúú:| Þ³‘)<ð• ´å‰}ø€ê×µZ­öÿøÿ3~ðÁPeÈÞQ.—ËT’°ÑÑÑÑD"‘0î6uÒétZ¥ÿ¤Óé´‹?c>fL§ÓéPèõÄ.YÐ ¿——§Y‰‰Z ¯;LFë“+dvù÷K¥RI%G‚“³ÿ*òpY¨ÁÁó9ˆ²°Q9* öˆr+ÀÔ³veø1nÓétZÅÇåxóÍ7ßôò,¼Ÿf³Ù,j]tï¸Iôb0´3nwïÞ½ËñšÝÿáæ'­@D*•J¨|Vw.ƒÁà3n>·µµµutttÀ×ffffBˆ æ2/—Ëå………Æ ‡ÃaþF£1;;;ëçoóÉŽý5ÞfF£qÿþýûŒ1–Ïçó====–eYüsW¯^½Z.—ËÍ–Oõç¶ýGGGG‰D‚ÿ¿»»»»¿¿¿/zkkk+¨sÁåË—/ó<õ‰D"1555å´Œh4½yóæM~tufff&ŸÏçeßuÄï:¨ÕjµsçÎã}ðÆ7‚¸‰ò ®¯VÉ {þáî3§VƒéééiÞ;:::x½”J¥R××€~àût[°&ë÷óëÞAþÿíÝ=liþÀñô/mQ˜' 1‚mnYœt§è„âP l ¡E{‹„Cs‡ÅFg#±ÝêVÂ>A+î®ÀFbWâˆb3(ºŽ5/×ì²khr¦HŠ(Óç_dŸÛ‡‡gÞ<ã™±óýTgÆÏ¼>o¿çy’ Bò[òÖKÂQuÄ}Q· zþtA¯¿×ßûÙŸ©öç#0Ì{Ò­/Öõžô“Nõ}é„GK\Ü-ú3`º¿‡½v@TÏjЀþΉÓï9¥9¶î€#GŽIS Ÿ›Q/èF1ŠØ)t $KkÀ$ÈL†ƒ\¿0ç/,ýü»/9»×5V ®¹\.wäÈ‘#A¶ :z%Èû2ŽwkÜ€°¿Ö°žŸ0€ ü`¢L‹ãL€?ýôÓOQüÀ(šœœœL²)k={ö¬þ zs×ÆÆÆÆôôôt©T*íÛ·o_|)L·$ÏßG}ô‘Û÷G=ª~¶mÛ^[[[sÛÇ7n¨ó=èßǹ„ìÆ¯‚lóïÿûßAþ>Èûrß­/_¾|©~öº§¢6ê¥¥¥$ºNÜš4ý6_ º¯@¯±•ÛÛ¿Õ.üÖÄiÂuJKç0L À ç/ìö¦f« Íœa[œ®ÐñÛn†Ùöüù ¾r»–zÍÃk”†þ½ž~ýúéßG]û÷Óºà6ɸmï—[°líM×x{;øsüí·ß~ë7ÀÑë·Ãv³¸ÕÆý¼C‚¼ÿƒ>¿AFQ™®­šþ÷‚e¢[­VËéÀÊårÙ­ bÐ}È‹¸²²²âVûnµZ-?/R©Tzûöí[5pI!jµZm˜MÌQœÃA„=a¶Ÿ•SýJÔ/â ²Ùlö¯ýë_2²b±X}út}E!©ë?¨´¿ Ôôw»Ýîòòò²úýÉ“'Oª Aéßc<´Ûív³ÙlêÿßjµZnžB¡P˜™™™bgäʽ{÷îýéOú“Z+þá‡~øË_þò5C“ÍþQ=?KKKK¦ Ó¯AßÿzúݺÇ,˲òù|Þk$S£ÑhøyŸïâýá~ƒ ³}ˆQXêð''‹‹‹‹²×n·Ûêð¾………õomÛ¶çççç݆Fqöü…Ý^ïïÓ‡Å-èõOZØógY–•Íf³úý©’µ “J¥RQÿ6›ÍfÕL½^¯ûýÞ´Öƒ×÷G¿>{öìÙ£gĦaÌrXcØí…ø­Û)Íd2™•••S͹×ëõfgggåPÉ|>Ÿÿûßÿþwµ_,‹jš:N§R©T„ˆæýÓhü¶fÉ ¢|ÿ‡Ÿ(‹Å?üáðó·{ÆoÊfý¢›š ÂîC/ ±À¡ogº1¨¿­bglfTA QœÃ0ž¿aœÿ¤ÅyýÓÎ`$Ç©'•&Œ}¹oYaÒ+RBìdN¯^½z¥wAöz½ÞùóçÏûy/v»Ýî… .D™þsçÎÓÿ¿Ùl6å»C­êïÀ°ï=€²\.—£áãôûjú÷;vìØáÇ«v»ÝîÇü±©ÂÔÅ>T¦(c©^¯×'&&&¼úÕ~Æ^¯×»ÿþ}õûl6›šššrÛ‡_QXaÏ_ç?iq^ÿ43Õl:NÇkRÀ/½þÎ;w,˲d˪Þú”Ïçó7oÞ¼©ïçùóçÏ_¿~ýZˆ[]ÕÂÄÚÚÚZ”­H¦·‰Ÿêõz]mÉ ûþúôéS½‚ÒjµZò¹­Õjµ AN§ãôž®ÿJ!öš†¼éA<^ÂîC/Éæ¢A#:M}@úoD)ŠsFØóõùOZÜ×?­ªÕjUo’ºö˜ZU•J¥¢×šgfffôVµ+RŸ±S/0m†>„°ßï÷oß¾}Ûïöaßÿ¦J]¹\.›F˜™õuoa÷Ñn·Ûn}”òàÓš%ÄöüúùLJªÕjUHêv»Ý'Nœ QÒ ×ssssz?¿:5nÚæ:Š÷»Ýnûé–•3·.‚­­­­7oÞ¼ñó»®kÄ©R©TÜ2!©\.—Óº(Q’ž¿0Ûÿøã?ª³›ú×£õù+•J¥[·nÝRÿ¯ßï÷?ýôÓOÉüµåååe5s/‹Å·oß¾Ukª¦õUÜösëÖ­[úl”²5K’—÷ æõSp[S"Hdj Bül Q9õ!ívaÏß Ûë5€|>ŸŸ šþÝ*Êó§d ±»òBü6666®\¹rEÍ´ô¨ÿ›7oÞTûÙMCÝÔ¾t½RmÍÒ[Æíý# ²BöÏþóŸzëITǸ7Š“õЇå™JDQ÷…‘¶0ìù º½)ˆÅ45gZ9sæL’‹]Euþ …BáéÓ§OÕ­mÛöùóçÏïöÌß«ŸzÜ·6˲¬O>ùäý>–“©¨¦ TSÁÕI±X,Þ½{÷®üöùÑ[‚æ-Ã~ÿonnnNOOOëqQÜ{×ÖÖÖô—{Ð1ÒQìÃeYÖåË—/«ÿ—¦(îaXaÏŸ×ö½^¯÷ìÙ³gê÷^CYJ¥RéÌ™3güÅðøéWS'³Â}¤DPQœ?§ÉœÒ¼ü±Ê4ªoâõ½Êto[=êÛeš.V­Õj5·)e…ع'&&&œ† ËÈ~9~_ÒïÝ~¿ßÿóŸÿügõÚív»jšÊårYþ~Øç'lžö÷¿›½êÐ Éi¬¦;'®ßï÷ÕSØ}ÈéæXÎår¹ÅÅÅÅArØ¢8‡a„=QœÓÔ›­V«å´—×”ÃIpš&YN?ªþ_@?ž¿»wïÞÕ's2-†2ŽL£XÔûÕk’—Qß>mêõz}ÿþýûõfk9ËŸ[íZ¶Xýãÿø‡:¹Õôôôôßþö·¿9.Â}zýê£zþ†EÎrúÞ…®êv»Ýo¾ùæ§ýŒúöi·¹¹¹ùîÝ»wòó°â·Â>?¦ ‹‚ˆ3ÿÓã(õÞ0@ýív:NG_ $ì>ä6^'¯Ûív÷ïß¿?ªƒ†(Îá ¿9èù‹úüËá„N¢ JSÿ´¬¹Á]XXXˆòº9Åó–dÙív»×®]»æô½>[›eYÖÁƒê5Àf³Ùœžžž^___wûýQß>i²©ßÔ- /6uüŒ.Ìó#ß^ïo§E­Âä^…9·šó¿Èu‰ir”Ûúéƒ0ºôþ[ý]¡?j1q ÅöAââžMTö±é—’̃ä=?Ì{}è©ó»Í^ä´íýû÷ïg2™Ìõëׯ° Á¨ëÁ ±Sû_^^^vú>è\íH$Ö¢ýûúüˆi&@9Ìb ?~üضm»X,O:ujXi }56}!•©©©©l6›•ŸÕ•‘úìu:ÙìîÔt>lr˜`š&[Õjµª6[¨Ã¼š3ô!jSÔ¸D-øúŒ›ši½¾T¹\.·¼¼¼¬fô²9=hSþ¸w …Ó¸F?'ÑiL*q/r²S>2Jȱ+ø©Á› dþ?œ £”ùŒ1SóÍÈõ ÀÏ|öA™Ææzuغ`¢H ®vk@ˆ÷'MÚÞ7a†¾/?ý_n‹RÐ`ÔyM8…„íæ€f܃»z£û)H¨­N+MÅ=ƒDÁmdïµÙí!Þ?ƒŒ€ši«…¯¥twÛµ0úLóÉ0Ç R/—Ëå>ûì³Ï‚lóûßÿþ÷AþþÈ‘#Gü6ïöÙgŸ¥½+ÀÏúØzAS”ônÝøbRW[ŽL…Mõ{º‘vÓ½w[MƒúÛjÅ'M­z+pª+^NýÆixب ¨m¼ù)¨÷­ü^YíÆÌ_ˆàsq8µyµ, áÞU%ӿ嬋S×V˜yFdúMÇç”ñ “ÓDj~¯å°øé{×ß×A2=ýÙUŸyõ>T÷iº>nqPjºõã‰òüƧ°×í@üœ'Åb±¸¾¾¾îõw^'ÜÄO¾´´´´²²²¢.!šFa?éíML¥þAïƒaÖr¹\nqqqQˆ9ʯ\¹recccãË/¿üR.[Úét:¦åBM¥s¿Ç„þ"W÷­Ÿ_§Žé:x½œJ¥RéâÅ‹…Ø97r÷L&“‘çL·¼¼¼,—±­ÕjµjµZÍår¹‡>”sü›æïo4r¹\–ŸËårY¦/—Ëåææææ„âÇüQO£©`ŸD¥C¦Í´Æý±cÇŽ>|ø°úwRZžÓ=—äiúýé•–J¥RQ× 0- gY–uçÎ;òóÅ‹/ú=>õÙUŸi!„ØÜÜÜ”÷ñG}ô‘Üfß¾}û8pÀï1Ç¡Z­VÕgbaaaa˜Ë~}1 ÅÅÅŸÊR©T:~üøñ8ƒ‘/’V«Õ2}ŸÏçó¿üòË/QÖ ÔõÉïܹsDz,«P(Î;wNˆÕæ¾øâ‹/ôíªÕjõ—_~ùE]¸FÕjµZihùb'­ëëëëê:ìBìdä++++¦Ì&—Ëå®_¿~]š/_¾|ùw¿ûÝïdæ^.—˦BÎÆÆÆÆ•+W®Ø¶m !Ä­[·nýç?ÿù[aJÍà›ÍfS®‡.Ó'ÓnÛ¶ýøñãÇr»F£Ñp*Øû­tDI.V–Éd2÷îÝ»'3µB¡P¸wïÞ½L&“ÑÁé>ªÕj5½À[*•J/^¼xaºç乊³ùZ¾jµZ-è¶j! ŸÏç>|øPVnß¾}»ßï÷…p/tªÔgW!VWWW-˲äç'Ož<âý{øÒ¥K—äy•‹%©T*•nݺuK~n6›ÍafþB÷¦ 7NYÐ’á0j AÓ¤¤kðI¶ým¿M‚n]jÚÔãðä7½Qô×…ipk¥pÚ§~|A›òMi6¥Mß§WÚõ46†×‚AN×u]NÇít?¨Ç§‡S,E©T*ýôÓO?éçÜiÔΰÉßm4A‚•õ{×ô¬¨yÒ ëǘžßAF¸½Çô|3ì3ïç¼ …ß“íç$ a~ù¸•Èã(¤)ÀC—t>J€(ºÔß”iò dS·uªáG=\2LÀ)ss›g"Š`¾$Gð„9–é½gJ‡¼6¦ïä½é'ýIo˜4xÅÚè]{!vbNž«R¹\.Çì¤G îÆÐ›¡…HQÒ …Bá믿þZý?¿QÙú4˜B¤#ó—Úív[ö½Êÿ“™Ðwß}÷iµÏÝËääädTùÚív{ÿþýûå,~zzGñÖg*T[7Æ9ȶ\.—õcT›»Ýn÷Úµk×âH‹Ð¬ŽˆÑ¯Å0Þ÷êóä¹J½ÕJˆt¼Û†Rh·Ûm½Ù\½‰£§ëtCJrª×Aö«7ív»Ýî… .„LòØ2M[ê÷ådj*Oc-Uï2’ýïív»-ÿO–¥w¸Y[[[‹²I^Ðãð¡´-¦S©T*N#Lda.Ì0À´¯*HËmšZõ÷b¿ßï:tèPÞmÿ7¬Ë>9¯©a°mÛ>}úôéA_t¦ ­n·Û=qâĉQì3C©T*=zôè‘ÔäœéMz¦HûQ$ƒ™œÖPc3n”d&étÄ­ý«¤Ó!DºÒ2 ôV«(òÙ}Eú† gJŠ;óâý­úk }ªž?þ|ØÌ?Íà Ã2ºÕáeÃT¯×ë¦j§ÓéDY F8úü &ãG‘æãuãgž€´°,Ëúä“O>‘Ͼ\â<éuM„ˆi`œ}›ú!„wl= „óØs7¦¦¨q£« Út_­V«¦n=¸.ó›ûyYº¥Ý4ôkã°é’f篎.µùfz½^ïã?þX•ÓjµZƒüÌhšŽÝÌy é L7†u.qÉï ýØ2™LFf7 %'¦¦¸¯¿þúk§õÒ2€ôò3Î_¯X¦½¢%ƒXÓ4±\âSë 嘆òÕjµš[i' SdøÙÚÚÚšà¥yô ‚ {üIoojq‘M\ê6^¿¾(jÀ,šÖÑ •…‘܆vÆ-ñ@鞉lºyþüùóׯ_¿vÛßÏ?ÿüs<)ßÆÆÆÆ7n$ãC_èçÉ“'Oôî*ýÝ;J#L“Š%@H^üºÝn÷›o¾ù&îtÅ©Ýn·ýοÏLŠÜär¹ÜÜÜÜœülÛ¶mšj[_øjÔE²,Ëú׿þõ¯ Û™â®ôQS¦)ÈM“%Qˆ€eYÖÁƒêÓ®6›ÍæôôôôúúúzRi‹‹ ¯ˆ[í_Ò[-ð{­-ãDöÚ úhÓ¼+~þFˆ z¹õnŠÞ6=:T_©*ÊQH—/¿üòKý¡eŽ}`4¨HÛ¶m?ƽ^¯wÿþýûòóáÇ;vìØ0Ó™½‚Óív»ËËËËêßè…(ÓßHCHbV8˲¬ÕÕÕÕ8ñ25Ï™  NDÒ¥T*•.^¼xQ~²’ëµk׮ɬL&“¹~ýúõqìöÓã#Lù«ÚÿoÛ¶}åÊ•+ŽypؽÀ«Ï!NúØrZƇS@gߌ’Ð˧yyH9½lÒé@üXÄ Ü….IØ­kÃÀ™&pqš˜G|(LéôšTF‰É†i–Úå€ …Báܹsçäç4-è¢ÏŠèU0ÐWŽòZ­*èþ*µ}UÁ’-†!•kà7㺠Y¦.•J¥G=’pý~¿ÿÅ_|1œ¤…ÓëõzI§coÞ¼y³µµµE !`X|ªÕjµÕjµäçn·Û=qâĉ4ÍÑ?.F¹ð ¾ºFCÍü;Ngzzz:ÉÌ?—Ëå~øá‡ç×<nX”À1Íf³Y©T*ÃH ˆG ÀÂÂÂËà_£Ñh¨-af_ÀÄW €mÛöüüüü(.Ü>ä«ðÕW_}5ät¶oß¾}8 þß³gϞɱôÀÙÈΠ®È'Äh¯.@¢dTúÖÖÖÖ Süª‘õa—ö¢÷“§a¢ QýŒ¤†ÌXYìgiii)®ÌXþV˜KÔ(FÅ]?~lÛ¶],‹§N:åwG¥R©tüøñãBñàÁƒ¦ñ…B¡0333#?¯®®® €{FCm®?yòäɰSО={ölø¤9Sûÿéû ¸½BÑjµZ²)ºV«Õ„¾œ¿^!ÊårYíŸ:À²,+›Íf÷ìÙ³'›Íf©ýŒq€mÛöéÓ§OÉX{½^ovvv¶ßï÷£KÞxsZŒ‚úõÿà´ˆnÆNœIEND®B`‚CubicSDR-0.2.3/font/vera_sans_mono48.bmfc000066400000000000000000000020111322677621400201020ustar00rootroot00000000000000# AngelCode Bitmap Font Generator configuration file fileVersion=1 # font settings fontName=Bitstream Vera Sans Mono fontFile=VeraMono.ttf charSet=0 fontSize=48 aa=1 scaleH=100 useSmoothing=1 isBold=0 isItalic=0 useUnicode=1 disableBoxChars=1 outputInvalidCharGlyph=0 dontIncludeKerningPairs=0 useHinting=1 renderFromOutline=0 useClearType=1 # character alignment paddingDown=0 paddingUp=0 paddingRight=0 paddingLeft=0 spacingHoriz=1 spacingVert=1 useFixedHeight=1 forceZero=0 # output file outWidth=1024 outHeight=512 outBitDepth=32 fontDescFormat=0 fourChnlPacked=0 textureFormat=png textureCompression=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 invA=0 invR=0 invG=0 invB=0 # outline outlineThickness=0 # selected chars chars=32-126,160-255,262-263,268-269,273,286-287,304-305,321-322,338-339,350-353,376,381-382,402,710-711 chars=728-733,937,960,8211-8212,8216-8218,8220-8222,8224-8226,8230,8240,8249-8250,8364,8482,8706,8710 chars=8719,8721-8722,8725,8729-8730,8734,8747,8776,8800,8804-8805,9674 # imported icon images CubicSDR-0.2.3/font/vera_sans_mono48.fnt000066400000000000000000000705331322677621400200000ustar00rootroot00000000000000info face="Bitstream Vera Sans Mono" size=48 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 common lineHeight=48 base=38 scaleW=1024 scaleH=512 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 page id=0 file="vera_sans_mono48_0.png" chars count=254 char id=32 x=48 y=245 width=3 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=33 x=38 y=245 width=5 height=48 xoffset=10 yoffset=0 xadvance=25 page=0 chnl=15 char id=34 x=820 y=196 width=13 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=35 x=81 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=36 x=152 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=37 x=322 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=38 x=348 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=39 x=8 y=245 width=5 height=48 xoffset=10 yoffset=0 xadvance=25 page=0 chnl=15 char id=40 x=920 y=196 width=10 height=48 xoffset=8 yoffset=0 xadvance=25 page=0 chnl=15 char id=41 x=931 y=196 width=10 height=48 xoffset=7 yoffset=0 xadvance=25 page=0 chnl=15 char id=42 x=925 y=147 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=43 x=336 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=44 x=953 y=196 width=8 height=48 xoffset=8 yoffset=0 xadvance=25 page=0 chnl=15 char id=45 x=834 y=196 width=12 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=46 x=1014 y=196 width=7 height=48 xoffset=9 yoffset=0 xadvance=25 page=0 chnl=15 char id=47 x=22 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=48 x=44 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=49 x=0 y=196 width=19 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=50 x=341 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=51 x=110 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=52 x=432 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=53 x=362 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=54 x=154 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=55 x=176 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=56 x=198 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=57 x=220 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=58 x=0 y=245 width=7 height=48 xoffset=9 yoffset=0 xadvance=25 page=0 chnl=15 char id=59 x=971 y=196 width=8 height=48 xoffset=8 yoffset=0 xadvance=25 page=0 chnl=15 char id=60 x=600 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=61 x=624 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=62 x=648 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=63 x=317 y=196 width=18 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=64 x=296 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=65 x=400 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=66 x=308 y=98 width=21 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=67 x=110 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=68 x=352 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=69 x=446 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=70 x=60 y=196 width=19 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=71 x=418 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=72 x=440 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=73 x=412 y=196 width=18 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=74 x=431 y=196 width=18 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=75 x=24 y=49 width=23 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=76 x=530 y=147 width=20 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=77 x=72 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=78 x=528 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=79 x=550 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=80 x=572 y=98 width=21 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=81 x=594 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=82 x=192 y=49 width=23 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=83 x=616 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=84 x=530 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=85 x=638 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=86 x=288 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=87 x=108 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=88 x=556 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=89 x=582 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=90 x=742 y=49 width=22 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=91 x=860 y=196 width=11 height=48 xoffset=8 yoffset=0 xadvance=25 page=0 chnl=15 char id=92 x=682 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=93 x=872 y=196 width=11 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=94 x=966 y=0 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=95 x=788 y=49 width=22 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=96 x=908 y=196 width=11 height=48 xoffset=5 yoffset=0 xadvance=25 page=0 chnl=15 char id=97 x=770 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=98 x=635 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=99 x=160 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=100 x=677 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=101 x=858 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=102 x=180 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=103 x=593 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=104 x=140 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=105 x=614 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=106 x=1009 y=49 width=14 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=107 x=902 y=98 width=21 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=108 x=719 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=109 x=216 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=110 x=120 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=111 x=924 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=112 x=740 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=113 x=782 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=114 x=279 y=196 width=18 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=115 x=558 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=116 x=131 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=117 x=40 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=118 x=696 y=49 width=22 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=119 x=54 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=120 x=456 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=121 x=504 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=122 x=80 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=123 x=540 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=124 x=20 y=245 width=5 height=48 xoffset=10 yoffset=0 xadvance=25 page=0 chnl=15 char id=125 x=522 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=126 x=576 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=160 x=44 y=245 width=3 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=161 x=32 y=245 width=5 height=48 xoffset=10 yoffset=0 xadvance=25 page=0 chnl=15 char id=162 x=945 y=147 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=163 x=946 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=164 x=985 y=147 width=19 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=165 x=270 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=166 x=14 y=245 width=5 height=48 xoffset=10 yoffset=0 xadvance=25 page=0 chnl=15 char id=167 x=336 y=196 width=18 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=168 x=722 y=196 width=13 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=169 x=135 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=170 x=645 y=196 width=15 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=171 x=803 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=172 x=120 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=173 x=847 y=196 width=12 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=174 x=216 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=175 x=778 y=196 width=13 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=176 x=692 y=196 width=14 height=48 xoffset=5 yoffset=0 xadvance=25 page=0 chnl=15 char id=177 x=384 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=178 x=792 y=196 width=13 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=179 x=736 y=196 width=13 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=180 x=884 y=196 width=11 height=48 xoffset=9 yoffset=0 xadvance=25 page=0 chnl=15 char id=181 x=968 y=98 width=21 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=182 x=425 y=147 width=20 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=183 x=1006 y=196 width=7 height=48 xoffset=9 yoffset=0 xadvance=25 page=0 chnl=15 char id=184 x=942 y=196 width=10 height=48 xoffset=7 yoffset=0 xadvance=25 page=0 chnl=15 char id=185 x=750 y=196 width=13 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=186 x=504 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=187 x=824 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=188 x=168 y=49 width=23 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=189 x=312 y=49 width=23 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=190 x=942 y=0 width=23 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=191 x=393 y=196 width=18 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=192 x=426 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=193 x=686 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=194 x=816 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=195 x=790 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=196 x=764 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=197 x=738 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=198 x=712 y=0 width=25 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=199 x=467 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=200 x=488 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=201 x=509 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=202 x=320 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=203 x=551 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=204 x=374 y=196 width=18 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=205 x=355 y=196 width=18 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=206 x=260 y=196 width=18 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=207 x=298 y=196 width=18 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=208 x=917 y=0 width=24 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=209 x=0 y=147 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=210 x=22 y=147 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=211 x=44 y=147 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=212 x=66 y=147 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=213 x=88 y=147 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=214 x=811 y=49 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=215 x=383 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=216 x=660 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=217 x=833 y=49 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=218 x=855 y=49 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=219 x=877 y=49 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=220 x=899 y=49 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=221 x=634 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=222 x=921 y=49 width=21 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=223 x=286 y=98 width=21 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=224 x=943 y=49 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=225 x=965 y=49 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=226 x=987 y=49 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=227 x=0 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=228 x=66 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=229 x=88 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=230 x=608 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=231 x=200 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=232 x=132 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=233 x=242 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=234 x=264 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=235 x=330 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=236 x=215 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=237 x=236 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=238 x=257 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=239 x=278 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=240 x=374 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=241 x=220 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=242 x=396 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=243 x=462 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=244 x=484 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=245 x=506 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=246 x=660 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=247 x=264 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=248 x=892 y=0 width=24 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=249 x=240 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=250 x=845 y=147 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=251 x=865 y=147 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=252 x=885 y=147 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=253 x=144 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=254 x=698 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=255 x=96 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=262 x=299 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=263 x=905 y=147 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=268 x=404 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=269 x=965 y=147 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=273 x=990 y=0 width=23 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=286 x=704 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=287 x=656 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=304 x=1005 y=147 width=18 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=305 x=572 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=321 x=504 y=0 width=25 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=322 x=726 y=98 width=21 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=338 x=867 y=0 width=24 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=339 x=478 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=350 x=748 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=351 x=450 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=352 x=792 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=353 x=468 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=376 x=452 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=381 x=765 y=49 width=22 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=382 x=20 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=402 x=0 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=710 x=677 y=196 width=14 height=48 xoffset=5 yoffset=0 xadvance=25 page=0 chnl=15 char id=711 x=707 y=196 width=14 height=48 xoffset=5 yoffset=0 xadvance=25 page=0 chnl=15 char id=728 x=629 y=196 width=15 height=48 xoffset=5 yoffset=0 xadvance=25 page=0 chnl=15 char id=729 x=26 y=245 width=5 height=48 xoffset=10 yoffset=0 xadvance=25 page=0 chnl=15 char id=730 x=806 y=196 width=13 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=731 x=1014 y=0 width=9 height=48 xoffset=8 yoffset=0 xadvance=25 page=0 chnl=15 char id=732 x=661 y=196 width=15 height=48 xoffset=5 yoffset=0 xadvance=25 page=0 chnl=15 char id=733 x=612 y=196 width=16 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=937 x=552 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=960 x=528 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8211 x=189 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8212 x=27 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8216 x=962 y=196 width=8 height=48 xoffset=9 yoffset=0 xadvance=25 page=0 chnl=15 char id=8217 x=989 y=196 width=8 height=48 xoffset=9 yoffset=0 xadvance=25 page=0 chnl=15 char id=8218 x=980 y=196 width=8 height=48 xoffset=8 yoffset=0 xadvance=25 page=0 chnl=15 char id=8220 x=576 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=8221 x=486 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=8222 x=594 y=196 width=17 height=48 xoffset=4 yoffset=0 xadvance=25 page=0 chnl=15 char id=8224 x=194 y=147 width=20 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=8225 x=173 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=8226 x=764 y=196 width=13 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=8230 x=408 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8240 x=243 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8249 x=896 y=196 width=11 height=48 xoffset=6 yoffset=0 xadvance=25 page=0 chnl=15 char id=8250 x=1012 y=98 width=11 height=48 xoffset=8 yoffset=0 xadvance=25 page=0 chnl=15 char id=8364 x=719 y=49 width=22 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=8482 x=842 y=0 width=24 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8706 x=100 y=196 width=19 height=48 xoffset=3 yoffset=0 xadvance=25 page=0 chnl=15 char id=8710 x=162 y=0 width=26 height=48 xoffset=-1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8719 x=761 y=147 width=20 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=8721 x=814 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=8722 x=360 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8725 x=836 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=8729 x=998 y=196 width=7 height=48 xoffset=9 yoffset=0 xadvance=25 page=0 chnl=15 char id=8730 x=240 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8734 x=374 y=0 width=25 height=48 xoffset=0 yoffset=0 xadvance=25 page=0 chnl=15 char id=8747 x=880 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 char id=8776 x=48 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8800 x=0 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8804 x=480 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=8805 x=672 y=49 width=23 height=48 xoffset=1 yoffset=0 xadvance=25 page=0 chnl=15 char id=9674 x=990 y=98 width=21 height=48 xoffset=2 yoffset=0 xadvance=25 page=0 chnl=15 CubicSDR-0.2.3/font/vera_sans_mono48_0.png000066400000000000000000001253141322677621400202120ustar00rootroot00000000000000‰PNG  IHDR͹ IDATxœì_hÔÉ–ø+Ã}ìàCû`´³:OÎØzïÃñ"éLÃì rq±ãÃuÝÅ0‰ Ëz}Q°"츸ã€iYïΙuHg\Жܙ0°,¦Õ…ÑM;pIÚûAÒïù=8õ›“cU}«ê[õýÓ9Ÿ';v×·¾U§N:uêcAAA$’|>Ÿ_YYYYÌÎÎÎÆ]/SÊår9èfggg×ÖÖÖ³Ùl6ŽzAA EÇ  K¥R©¨žôÿ‘&ðbseee%ŸÏçã®—-i¬_ ««««Åb±è²|_ú ëjQ™°¯\8zÅqBAA°‘W©T*.Ë'±‘à;°®aq“æ÷‹f¨W Î ëЈB¿e³Ùìâââ¢Ì "¬W=ÓõAAADL±X,®®®®®­¹Ùƒ€Ø(@YNÛbYDšß.`E::Â,h£ÐoP?óß«è.¢4 ã§R©TðóÊår9LùAADÌÀÝ2—a¾ä 6¾ÆO\¤ý}à_¶Xuq^>j~G4`¡s:\;ЉèQ9‘h&‚ ˆ 4úÓ¶ãGqhi\,czá}T‹f%Ì{8ˆ"#à¢1­²A¨sc@(Òƒ ‚ 6;wîÜIF1A“Ô±R,‹.\¸`ú»¤¾ A ñàÿÓnç/|üñÇë~7ŸÏçGFFF|Ö‡ðC¥R©È8Ð9@‘žgmh÷… ÂQÆbÒ§Aôªp]‘~ƒßߨúÚÖ²$€´ËIo#;’V`DP"t¢êZµ5 ¯q VúQ\C¬ˆlåFdÆ®Ôb@µðǤm"Í>æÏq%×¢þLŠ\c}ïBžLä×Ün.ÚV$+°Ÿ{Mâ~“ ð°¼0|z'¬Üë,ÚužáÒfK“¾Žê9:}Àå6hç—Ôw6·'@âÜýZú®cÐQ[D}…]æÚðN˜Ê”ËåòÌÌÌL˜262o.*Ñÿ¿~ýúõË—/_òÏ™L&3000¦Nï¾ûî»ðó?þøc˜òˆd€£r\*ÙF£Ñ¸~ýúuø·ýû÷ï7-ÿàÁƒ …Bîv»Ý³gÏžít:õ´Et¶Ì—‚Ïçóù…………\.—óQ~ܼ÷Þ{ïá¿år¹ÜÐÐÐPÒžãJ®O:u ögRäºhµZ­cÇŽëv»]þ·B¡P8þüyßg³ÙìÄÄÄü[³Ùl?~ü8ÿÜkrçûäóùü?üðÔó* …Bá‡~øáÂ… &'''c¬^¯×§¦¦¦Lê…ÞÙ¾}ûöþþþ~Wå‰hµZ­­[·n­×ëuÑÿW«Õj©T*¹z^šôu’ž#"“Éd°®ÁcH_ì½zõê•îØI+™L&óàÁƒkkn@°ïŸ>}úÔE™Œ1vóæÍ›¸O&'''7LŽÈ#†;.›Ífçæææ(à AWàÿ—-8ðÂ.¬Ð¹.Ï'"¹£ˆ…õˆv{|´WؾyQmë¦ò4›xIƒ<ý><Õ¢ãSxìöJS@T÷0»|NX¹ÉP’ú,íGǰ¸¸¸øÉ'Ÿ|ŸÅÿ¯R©TxxLˆêŒÇš£À>ÀcAÖæ²¯®•CId³^¸pႪ¯\6¶…î‚!ˆ(ô~?Þ>Qì:âç¤M_Gù]ÀÎ;wr;çÂ… øî>ŸÏ‹ºvQ¯98"ç‡ÎøQ9|Ùj°Í£ÚO„½ˆ+‘Ïv€¯Jq`¥€ÁíïÒ`ºKªR<.ë)zvξږ+û,Ï•áo³«f÷_ä2 ã Š¾‘Õ–å+Ô+MrnûÅÅÅÅ#GŽòìB}=GdŒ-zlWQÓK¤í¢¼×ä ª÷±Y»pœD¥wàûÁùÀµ@ô>þóŸÿìò²ç¤I_Gù]@6›ÍÊäAäеzÕÀ˜»wÃΟ0í£S'“h'[áp¹½QÀÊ(èü¿jÒs¹i²À : ó»0uÅî·ß~ûmÒ ¶…IaŸëL ²‰Mç;¦˜Fˆ¾¯käÙ†ÕeºŽ×Ye{Ù€uK¥R©ˆŒ²°ïëó9&ž}ÕA’è%cúíÆ@ë59ˆâ}LûZ¶‹g*ŸQé؆°<×ÑûøØqì}ÕsLðûÐ^9tÇLR¼eºÔÖ¾qµÆ„娖eFÕÖ¾çV׫[pVMÊÌîÝ»wïØ±ccŒµÛíöÂÂÂüÿM›6mÚ²eËÆÞdÖ]^^^–•…oزeË–M›6mrQOY_‹ÅâÝ»wïf2™L½^¯oݺuk«Õjé”Ùh4Û¶mÛÖl6›…B¡pÿþýû¦B›Í®ÏÜüüùóçÿò/ÿò/<t&“ÉŒŽŽŽš”5° ›ÍfsÛ¶mÛFCç·­V«µk×®]ª6„rovàÀ¬Å®²¢v:Îôôô4ü›*Ã4Îüßl6›ssssAÏ)—Ëežšÿn×®]»tä°V«ÕÆÇÇÇùç£G…ŽƒL&“aLÝ.ËËËË0ûxXpy.ÇrÜÀ6eìM»v:Ôm.Þ×çs®]»v­Ýn·ùçB¡P8xðàAÑw'&&&`=æçççuÇ·ìÌ_£8¬qÓ à ÷¢ÌÛÅb±xòäÉ“üs»ÝnŸ9sæŒî3¢’ѸÈàíâ« Q¿O>ŸÏïÝ»w/ÿ,²y0çÏŸ?/Êv><<}ú´LÿMMMMÁÛ:LnE8'nSȇß5ave„ñîȼŜ üØ¢]PÑû‹ê«ã­†e™f,…mËIªWß°®"ϰ Q{«¼ÏA9¸êxŒyuÛ¿‹¨î¶ãÒEÔ‹*Ê&®z*)ÈtÖaß×÷stv1lÅé$ž”…’btwtç±?þñt½«äËvÚÃϵ»>å@exs¸> š3lwÙ|¾OPTïozÄ4 ½ƒë Ër zóF¯èë(žc *ƒk“œ®#tôgqq}nU†‰pt1WèíTçJÝ:ÀºëôMNçÏŽý@š V“>G÷½u:Ò¼˜ 0‘a)Zé8ÿ@ºJ¶;¯—©Â’çqyÎÇ'²6Ô‘ü^ª6TÓ€me’cB·]u~gºèªŸe!aqå`¬7oTQ9lLŽ%á9:ºßf®‘íæ‡AGv‚®Žl‰ð¹y 36]MIƒo¾ùæïéȺ¯÷aÌ,„ëÕ'Nœ°]ÜD¥wàûáùÂ¥@õ>.Ï÷’¾Žâ9>&zÊ¥ ȱ„N›¨~kÓO¦«ªç›ÎUA›´Œ™Û7nܸ« )gOtpuŽ„1ý÷ŽÊ´p j‘-¬à{›z¹¹pãÿã¿Õíêç¨vBm˜x¼©v&ß ’•ÿŸëùݧ@Tþ'Ÿ|ò ”×;e.ä@a§Šßµ•uïØÙâ~WÙgÒgQé(g²¹YTSTeÙFg˜>'úÚ÷s|8LÖ+®Öa"[1ÈÕ­£èw&:$Hÿð6¯0s‹1¬£ÇDs¼Ê!‰ÜàRéøÆe¸„nYQ9‚¼S"Á `—Š?¾/T>6Ї ]Ù zWO[œˆÚ£r¨þ_§ ±,ŠúJæ´u¤¨¢lw,T»-Ùûp¥ eW6¡àßêŒ#Õóu&,ã®äX5yèNH¸ uš|]-£zލ,ÑbE÷º‹1Œ¿4®D‘ìû0.|;‚ÆpØplÆÜÊcodáÆ7tr²¾Áß³ Wuñ>Œé3‘9¹må$ }ävéPÕ7lé>Gçÿ7Ús|8Lì#‘ü˜Aη••••‘‘‘™€±àIE6ÑÊ<ä:}#{¦­F÷]x›p¹’½CØ÷ѳ6ïÕsTÏ»pá›òuʺó­É‚ ÍçÏŸ?·ég|;“;±\Ù*.å@Ý5l_]]îã}tê«ÚH°‘“¨ô»¸n:zTçÙ:ïƒÛ)Éz´×žãÝ1ΘÙâWÇù&k'Õ÷m~c¢ƒu×oªM “Í—¨×²¦b¢ö[gé-2ã~fff~¯P(^½zõʦá}ÓjµZ=zÄ?÷÷÷÷oß¾}»MYøæƒ/¿üòKÝßÂŒì¢l±°ìåååel²/^¼x±ººº*zF6ûK6ÑÕÕÕÕ‡>”½?›—Óív»÷îÝ»‡Ÿ¥“ÙWÏ´®ªcog~ôèÑ#œEg =|øða›:ùö…iÖýr¹\cLüÞºmáÃð ¹Ýn·;222Ò×××7555eRGöïß¿ÿæÍ›7s¹\Îô·°ý“»ÙÙÙYÞV2r¹\îÖ­[·ø-"Z­VkëÖ­[«Õjÿ_½^¯÷÷÷÷à °¼=¡^l6›ÍÍ›7oîëëë«Õj5UÊåryiiiIÔ6ccccavN:uŠ—Ûn·Ûƒƒƒƒ}?Ã3Õær¹ÜÕ«W¯æóùüåË—/óßV«Õ*ÿîÈÈÈÏ8}ùòåËANTxÓcŒ½|ùòåëׯ_Ãïà1;000`:‘EõN«Õjݾ}û6ÿœËårÿüÏÿüÏüs·ÛíâÛ0dà Õ2½o‰Ðùžifë¿þë¿þkþo.'Ar›d.]ºt‰g~æt»ÝîÙ³gϺÈÔîRtÁYßes ¼ÅGv ÆÇûè<ûêÕ«W¹~º~ýúuÝ›dD¥`Vþ06P:ïƒçC›L罦¯£žÒ ¶“U7‹Åâ§Ÿ~ú)þ{.—Ë Á¿Áµ£ÎzQwcV¶~›šššê“ šÇ°ÓŒ×ÑÅMºd³ëoÑѱðFWX]˜&à„æª88¡™\Ï0xâÆ‚ ëXÀWBà•ƒ/_¾|ù¿ÿû¿ÿËx¥¼z±·¯ÄÿßG¤Xuák¡0444G¢öÁŽÕtQÃÛ0Hfr¹\niiiI¤˜dWVé¶!ô¤ÂE27öñ¢V—N§ÓÙ³gϑ⅋ÆL&“Ù·oß>þ;¸@Æ`”eYBG |/X.¿pË–-[àÂK†hR)•J%U{òEóž={öè,4ÊårOøÃþ ““'Ož´‘e¨WºÝn÷رcÇàä~üøñã|2ÁšjµZ…ýÐh4§OŸ>ÍØ9=uêÔ)Õ³ñµU"§'Ö_6“oTψ—“EŒîUºØÑ«ó=[G·éõ®I_õåWr`ƒj.ö…É¢&ê÷:»^¯×]8ž£ÐØ^9æ]¡ó>x!k3ö{M_Ç1/¤ ¼.Á›|ø»W®\¹’Éd2Ýn·ûÝwß}ÿßd*jDÙx£ˆo€ÙÚÁ6@»–±7µ/^¼x¡úy­çØs.Z,ÁÐ ‘7 ïB?þüùãÇë<v´È[ Ù&Ž, p²‡»K>|Øh4ÏŸ?ÎØúÝ!ì)•í*@á9|½”*o;t „qô$‰z½^ïëëë³5ʹ\ãæ°åêÐh4óóóó¢ÿ›žžž¶Ù‰)Él6›…w~Ë1µZ­6888%SxÈlOèl01bEõññññ .\8wîÜ9þ·L&“¹råÊ™!ÃQ¡î‚ãTtow§Óéœ={öl·ÛíBM³Ùl^ºté~44U'‘g[dXt:ŒL2³Q=GTžÈSßn·Û×®]»f[® •£×Üâ®´ƒ‚Œ½éw¨'µ`G&‘ Q¾Ô}2ç¶)Q郠M0šJäPó>xó#©z´×ž“4`gЦ co˱jzóæÍ›…B¡Àc§OŸ>}ãÆðÿu#¢xõêÕ+^wÞ ŠráÏÁ€º›©&‘ç:H¢Ý½Í›7o†^b•Ð%%„Oœ"¯8\dŠBZðBY7LŸ?w´¨“á·‰c±õ“œì¡GîéÓ§O¡³@æ!V9ào¢0H9Åb±¸ÿþýü³*D ‡ÇÀH‡4À•Ü9³9›& KgìC$*Czzz:ß{³Àœ›››Ó-#hü`›Ê˜lµZ-¸¸Öz''''ùßÃ:Q&&&&àP­V«\gÖjµ<‚P( 7oÞ¼iR>ÔY2#U䤑9gà¸Wí,šx¶±ÃÑdÌFõµZ­ÆPpÎ;wÎD°Ñdz<ÈnA9fÌÿŽu”à£,±±±1—G]ÈA’ˆâ}àn¢Ëò£ÒÐ^S횆Åä}𢠉z´×ž“vtŽI0öfÓƒ;S¹‚¥a{®yä%Gýf# Ÿ‹§ä¶š‹ç¹GM˜¬)]b`â5a’¸Â¼`ÂRìÇ»ûŒ½}6{kt¼¾¢²\ÿçÈŒFn\ÂE=¯s&“ÉðÐø^¢¾-ö¡3Áô¬)„?[Õ–0”+h²Å]UHW”rÈÛ¶»ŠF£ÑضmÛ6îÌP­¢6ÄNßçûU˜,0u(#:á˜8dRŸX ¶¥è8'(#/î#Ñ®;Éu½xá@ãÑÅyV~©êÜ&a˜Q='iLNNNŠô,§R©T‚¾Ó Àså"&&&&’läËæ&Øw™L&óàÁƒ"9ÀN߸ÍëçÏŸ?Ïõi½^¯‹6Špx²Qéh¯™Ú¿&ôšíµçlÊår™;‹UÇt¬L€ãÛg"}‡#nÒÙ¦»¦ÄΗ°9t½FI6.ÞÇgÌ{;‘ Tú&^ß óÿŒ­ß 2q,0¶^ø{A‡Œ(€G!¸S"èì_ìãð(^OQ´„.üÙ2'åÒ‰Ž€ïè:äÓè0Ñ=+ó“'^µ!_t‡=߯ƒÎ-8wfffFõ}œôÎä|™éø¡Jê‡ÛR¶¨âº,zCçŒ"–ÆûôÓO?U9lôqPô“)xg[Õ'ØÁh†ÕsÒt6ò1Šwý{QèõgøçB¡P8þüùèk§†/ü{ÙIS,‹'Ož<ÉØzüøñã:¿Ó9…>ˆêü?~cé×£½öœ(ˆ3´Ó1«.ÁëMwÞˆ¢Ðƒ6º‚®ñÜH9t3' ¸@Â;±|Ñn·ÛøÃþÀØÛN8E^_™×Ÿ?-~ Á"ÚÝÑÝõãï ìxBÂ+[<ðBuíÚµkÜs´(Á'8ÕŽ#å-’0?~Ìs0f—×¢6 ¢Ñh4®_¿~1qâ5U–J¥ÒÖ­[·¾~ýú5^ íN‹®TIÂQ€wæÇÇÇÇeIý*•J%hQ%:¡{F‘±õÉ÷ç€:Y6Þe†~_Æä;¤0úK¦÷EÑT²k‘~TÕ5Žçô"xAÜ+à\ Œýr~ÎWŒÙ'ÖôE>ŸÏ/,,,ôÒÂ;öÿê¯þê¯nݺu‹'SÝÈ`i•>À6ÉØØØ˜¨ü°Q¢tèÐ!•íÕF0|¿­Éfƒ+(Jl#aäÐÍ\,ƒwš(g€+Dá8Ô‡¿4Æ=zô¨V«ÕÚívzñ.¾Ï¬¯¦ˆÁìèíä!;PÙÉŒzÞv…B¡pðàÁƒŒ­?KÍw;U÷ãÿ;xðàÁB¡PPµ%<#­uÈv4£–CQê ×£G…ÎŒ 6,—ËeÑ'Û–fìãJ”Su Ÿ)å‹jØ{k2)†¼Fã?þã?þƒ'ˆ‘å1Éçóù£GåŸñ3xî-lè>>—‹w0á¾l¼‹nÅeö•íÂ+ez_MeŠŽÓ.ªç$YB1 —ÝN§Ó9pàÀï¾û‘‘‘¨E -çðóå8÷GR¢Ã8pl1&¾Åæ†Qå^‚$á¬+çÈ‘#Gl¯ü Z`D¥ð˜/\¼NXv¯éë´Ï Agë}Âå%›Ífïܹs‡·cœ9M|l8‹¢ÄâÛµº¶«kdäˆ#q‘ p¨÷ŽBî?þø#üÿŽMÖרÀïõÞÏ0öö¢.Žûûûûýë_ÿ:6dïèµZ­™§6ž¤Iuïe˜[ð­8ŸCÈÚÐäw0 ¨ q–eÑýïðŽy¸…K¾˜Íår¹;wî܉óü¬hB„ý­3yccsâĉA,£Z­V±Ó±V«Õ @%ƒ:¹!Μ9s:0''''ËårŽÑxç²#òù|þ‡~ø/˜æççç?~œ?crrr:íàY@•3ÎÅXÓ9†Õs|‚õ­Ï«”:Nç·¿ýío{%ц–sðùrœ$ÖWN SDÎÄ …»Ï³ç®U•Ý21Mºœ&½£CšÞg#>ÇxCÏçÙzü,õ ©011$ªHpÜŸa3àC»ùóÏ?ÿ<\íÜ¥³Ç |Þ7(”8iÀÐf¾›Éÿßÿ f™4ygÜ^ØØú]ø™åµµµµ?ÿùÏæeŠvlá»|öÙgŸé>Öuqqq *ÎÄ åÂrTçnpy¦mÛ$)r*kC^WQ1¶þxÉÊÊÊÊo~ó›ßµaP™øÿáØµ5ì0}aó{Ù5w<ŽT·&à£:"Y6y'Yûâzáïá6Ñ9ƒÛ—ùÅ_|¡zo °nø"duƒÒíkÜ&}èë9¦e™Ê5>j£Û®.Î+ê>ÛaÛ #’qÙ¸ÄóŠjüêâR¿©æ*Ýï…ÅUÿˆÆ©n{›Ègõ–3“ñ•Ä÷¡çDƒÉ ¿¯©NéÍo¾ùæ60Ÿx,èÚ\øýÂêj¼ö€õŠ#€ÈžÓy?,«¶6,G; ì q£¸·‹G4ˆ’åñ9Û¬¯øz2|N¶§N’8Á!/S6BìÝ»w¯îóaB²B¡P¸ÿþ}(tµZ­ŠšÍf³÷ïß¿_( A»Ðóg“™z “’ØEÖ†¥R©¤ ׄdž†††¾þúë¯Umw…e×&òëù¸·›Ù}Pn¢Ž¨€žjÑ–øþê±±±1™ó+Ê[0ù#ïþêœ×„9!{³SqäÈ‘#Œ½¿¢HN§Ó9tèÐ!Ù­Ýn·{ìØ±c\FpÎŒ*0¿4·Ø³¯Šäˆê9¾± ܨ¹ dð£Pðo²°U Ê­’T¢ ?w¾}‰1½pb“)ŒE§pT‚‹SDôšíµçøÛ º7G¹xco’3¦Nú'JPé#Gj†=zÍ#t"¢ç+“]ϱ¹%m@ZoàÀEL&“ÉìÛ·o_(Ás°c>|\&ï 3}‹~–Iˆ;¾B¡PàeŠ’æÁ#ð»:ŽF£ÑøðÃ?ä ØŸ~úé']c±X,þôÓO?ñxàÀ²!œ˜ÌfàãœI¹ß5l>yòäI.—˵apQ½oß¾}\©ÈB¹D×AF|¶Ì™ƒÏÉçr¹ÜÒÒÒô”F}=–JYãc*º™ÉñÕ€Õ™Zî@¹šÍfsÛ¶mÛðïjµZmppp; ÆÇÇÇU×éàv“q‹Ç«* 3ªçøF÷¼'6l*!<¤þMæÄdìmg!cÉK(Ê}RœÚAà…•ÎŒØ¡3???¯:º•>0¹^. ½¦G{í9¾á›3ü³ÏkKE!õAIÿðøôáÃÇ¢TÇ…uàGÄ‚’F ÎW¦“—Fäì‹¶ ­7pð"æïÿþïÿžŸy–•ß¿ÿþ_ÿú׿æÿ§“‰ž¢ßA…¦-egíD^9Ù®¤®c£Ñh4Þÿý÷y¢D~qP(܃d2™L½^¯‹2ªCðDksîï>$é~×(Ú0èÌûî»ï¾!?ÿ IDATã2![\ˆ²GV”2gN©T*áE.¦Ûív?þøãe;âºÀk0e;ˆPYãñůj„ßÇçîEÈÆ¯ŽžGšpT2$J’)KˆÈ˜xWÀdÜŠv Egâ£zNà™ŽÂ7:‰D/^¼xq#8 p6g; ãN±²…½HîqRXL>ŸÏÇyõ£èV† G§È¡£ßqê¹ zMöÚs¢Ï >¯-ÅQŒ©×¢ñ)‹2µntñ¿™& …ÀdÇaÊñÞR奵}¤ÄynÐøü„ì …ì,¬î™"|~Å×ùѳ‚ΓˆÎëÛœy]'C÷|n—0ç~œË mÈXoå`Ììü«ìÜ;LxÈÇ‹íù)¬ðøÑÉa!·ª÷SÉŠ‹³Ìa°=ÏÑ9+Õstp!׸Oñû¨ú\ô<þý wê…¢yL÷=Dó»‹<¶åå‘ÉA~·éWWýcª¯lì’¨ôéyk[Û£×ôh¯='*Dú ¿SØ¢2Tí'Ÿ:í¬Û®²¢ºª„B$La™KáÔIBfKÜ‹$®¸Jþà8-б<Â1cSzËxœ“3G×ycš¸H˜°'è9¾ÁmaÓ?:MTÏÑÁ‡\«X\\\Ô•S€-6:ÕE»‰Æ©,àzØÎ®œ*VWWW/^¼xQwŽŒË߇_¬S_ˆNF¥L‡²ÅŒ¨·{MöÚs¢ÄD†tÚ„4E·}¼GX›†ë¡ 1§À´nܸqêÿHla\ɸ‡-&žÿ0Ù¡B qØED$@¦‹Ž¤ìŽ»2Le¤ÑieKâÅ2´s§”±·Çnd6H^uÛL™$ƒ/TDý‡|‹ôMß•Õstñ™-ÃǪÎwuææ´;pýmì‘-Ôs\¶ŽQ—CŽ/XÛE±Îs8¾ôi–x›wí5=Úkω—bpãw­T*“…¨‰n±q¸Ø¬ƒï´‹»ŸÓ³ËårËD$lx$y7U…Iˆy˜rÑ.«Éÿ›b:i'1š#Ìn¨.qî’ÆH©Ë"C4Œ|¸”µ´È­­ƒTµÉ®hÒˆºMð„Æ9¬Úɉê9º¸–E‘¡${G™Q¥;—¤Ù`*.Ã…®s)2#\&Ÿ¢ñvs&ìûð65•]Ó~ŒJˆú$¨Ml½¦G{í9qv·~mm½ÌªÖ*;ܦ=LÆ‚KÛÅf=wËê õb,€XJHÊ®7Añ6|ðÁ•Ÿ‰».A¬'›Íf/\¸p/,{4:\G¡Þ {ÆzYp‚H..ÎÿAA¤ðAP4–6!PA¾0Í Ó«Êž ‚ ‚PÓ뜈Uö½w‚ Ú½{÷î;vìàŸÛív{aaaÁUE ‚ Là÷ÔÃû¼{söƒˆÂ»––––r¹\nxxx˜¢‚ ‚ ˆ^cttt4“ÉdcìË/¿üRö=¥ ›Íf¯\¹r…Äc·oß¾ÝjµZîªJaN©T* ¶Ûí¶îovìØ±c÷îÝ»}Ö‹ ‚ ‚ K>ŸÏ?{öì…Ù¦5û?A½Oе9”+€ ‚ ‚èU`Þ>|«ÉÜÜÜÜÿ_Ç›dK¤³ÿAAA‘,TëúuùºZüAAADr™U:gúçôb†D‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ˆ˜(L”ó çKJBpxÕÙÚÚÚÚììì¬Éÿ«(—ËeÛßš‚±‡¹n]”Ôݶî>Û—HïÄ]‚ ‚ ‚ ¢¥T*•ªÕj•1Æ&'''¡ ›Ífïß¿¿P(ºÝnwddd¤V«Õâ«m4ÔjµÚààà`»Ýn3ÆØØØØ˜¯„èW¯^½šËårŒ1Öl6›»víÚÕjµZ¦åd³Ùì;wîð²8>ëNAAA¤ ÔN;ÔÅb±¸ºººº¶¶¶¶ºººZ,‹îÞ`}³ÙlÖ¶,×ýGA$QxS˜p)b=0ì­•9ÉAA¼MÚ®j í"—ö‚Ërá»âÞ§¦ ráèUÕQ~ø<-àü=Ƚ¤ÌI~‚ BË3ä½´!ÂîÔ3¶ÞkƒÀºåpèU{Ûx.úHÓõŒ èPñmB±ƒ'Ê7XA¡E î—^šôI~‚ ‚ËÎ;w†]\º(ƒóñǬûÝ|>ŸqñܤâQذ6ërzÍ' èåиËlë墜{z=üBòCAD…* -igëÓ†hñ´Qlb=xœù[.Ö3¸×õwyË„1Ø+cê! î(Ã#dŠ[çL”ìLUPôªÀÕàt¹€Ãò©C/NØPEù~XñÙœ53Õ;`ã"ÒçQ† ±qÀ¶š <÷™ü6<Ïù,;*T ŒÊF³@R…žS¹âr}¯ß lû”MWë|¼äÈ‘#G\aLݯ‰¾0ŸÏçàµÍf³¹mÛ¶mF£g݆‡‡‡ƒŒÆÝ»wïÞ±cÇŽ¨ê”dòù|þòåË—ùçjµZMëu23333XÉ¥™|>Ÿß»wï^Æk·Ûí…………¨žýã?þ?ëŒ+̦M›6mÙ²e ÿÜív»ËËËË®êHôÙl6;111w=7¨’–¥Å)Nô&\6''''u3999Ù‹› >(‹Å'Ož<ÁWÞÉë{PséÒ¥KÍf³Éc¹\.wõêÕ«®Ÿáj=m’n·Û={öìÙ?ýéOº~ýúuÆüÕŸ“X€èNËf³ÙLŠž ’A±X,ž­V«uìØ±c\VOž|øþí½÷Þ{O÷÷Ùl6;000À?w»Ýî½{÷#‘n²ÙlöÊ•+W2™L¦Ûív?ÿüóÏã®aN±X,Þ½{÷nÐΟH?/,,,€ðM>ŸÏ=zô(ÿÜív»ÇŽ;d»NMMM‰×6LLLLÀ1ÂC‹]Ø]>ËÖ·oMÙjµZçÎ;ÿ6111AQA½O£Ñhœ>}ú4co6ê\,¢]­g M"+Ö¿P(Ο?ަέV«µuëÖ­|~ܺuëV®çÀ¡SIZü3ö¦>‹‹‹‹Œ©À]Õ¹¹¹¹(ë˜$þò—¿üeÛ¶mÛ°à¥809G=šF:ª¢ÿç<}úô)ül’çÙX]]]}ñâÅ ×u$ÒËÁƒò9e~~~þÛo¿ý6î:f@ƒ‰ÿ­Z­VEsJ£Ñhô÷÷÷Ãh-ßg) ‚±·w§aXoF£ñÕW_}æùår¹ £LëWÙºàã~·oß¾d³ÌÍÍÍñpjÆôŽïþÈd2™<ån¸Ûç6}}}}¿ûÝï~¶ËÖÞVĘY´Ž2|÷Ýwßu]?¹\.÷äÉ“'Aö¯Î•Þ6vtÚÊ5!´ \.—áU'{÷îÝk:Yà¤)Ýn·ûá‡~÷U2à‚ENd²«*ºÊÈÔ‰‚“Qf>N{ý}ƒÛç³Ï>ûLt=¼¦¦R©Tffffàÿó°)—;X.U¢»xm F#ªâØí×îL¹6J¹“Á•3àÞ½{÷àk2‡â9ËŒŽÁÎ7èÔá"ÚçÇp™ëÄgÙ¦˜Ü„yýúõë—/_¾äŸÓš³i#Q«ÕjøzÌ ç*º`óæÍ›áÎØ›È[ý•¶ru±rd³Ùì;wî„1nðÑÆóhƒì€MRµV«Õº}ûömø7]#OPóóóóQ;P|ׇ™C£†'úØ™ÃRu S<âUÆÞ^°\»ví7`ƒØ”Ëå²N@Ã>곊"D‰z‚vêàŽ‹ja’Dù!ü¯êév»]¼nC§Óé:tè\H^¾|ù²l'k ë„€2lº#ìS?Ã,ÅÐX]χÁ;<Üé ³"oÚ´iÓ–-[¶ðïØìúaã?‰á¿|!¹´´´çwGy›ï_QX.glllÌ—î„Î[Ãϱ™L&ƒwµEE¿àܦ¸8Guçܹsç\ȇϲ]&j1M‘@켓En𹣿¿¿_¶þá™÷ñ¼£:–¦rá\t¤‚óŽÍ>‘½Ag€ÇÇÇDZw»Ùl6Ã^—‡Ï*BtϘe³ÙìÜÜÜœNˆ$>&©t0MÑW´4ÔŸËzX¯»(#®îî³(L¿3žØ°‹C áž™™™ÑÉr û¤ìœcR-RpFN WòCø_çòš*|§t.—Ëݹsç–l87›ÍæñãÇ›>æP- 9Õjµ*[ ¦A?‹Àç~{ èÔÁ›!èH)•J%üÿQä``lý]ÒØ åØD@£ÞØ ‚;lt0–[ù‡×‡2övòÉ_ýêW¿Úµk×.‘‘tÎ9Èø×At5¦ËÐ_eÇA§ÓéèFhÉáÅ‹/VWWWùgQäF©T*™Èe­V«A]#³—ÓV® Nn0¡P(pXÿ»Î¹J<©‚leì…ÌKÍÃ*MvWð1€b±X´Mª†C uÂ4qx¦Èé•à«þŒ­7>‚€“*— w:… £¯T*lŒ×ëõzЕT,|vP5yÍÎÎΊBEUYn]†ÿ‹úÆv‡ +{ÕN^¨Ú5Iò“4t2ÑBùÂ"ât@£Ý$[¼.8,Ï[aÆA}”$ﲫtOý%.r•Ä}þWg·_ ‘típ±®»ùR*•J¼\™“S( ¯^½z%³G06Ç`°Ûtª+ÁL2ÔÛ²QBÿ9”¹ã7¿\‚®d+måFîPqòäÉ“¦!d¢ ¥* —b“hø‡ø‡°½S]”¬)(L‡gÆyÍUÜõeëÅrÁ½ò*Ï{Ð;™ªÕjU´3cL€§“K.Œ¡œu»Ý.W–ªV›„ð¾PµÐƲvgÒ•üñ#s|…«NMMMÁ…Ì`sîF®û†ùCL‡qëçt´É6*`ߦe§•/ÚUa¦|óÅÄ1 ª²[?gË6xðܘY„cɺbVtüÀ•¾óY6A˜€€¹Â&ét/–ûŽìÜ{d›ÍfsóæÍ›EßÕ5L¾ûî»ïàKe2™Ì•+W®Ø¾ÄàDkA˜(;x `ß¾}û³ß)Á^jÕ"‡g&!‘[”õWëæà2W 7Þ§»óϱÝÁ’ýŽßþþþþÿ÷ÿwÆÔ;0ARÂÿ98c·,Ô z@mÆ[œòCø.¾}‡«ž9sæ ÎðÅ_|azî_” ‚C‰Ã8Ó>¿0æwÐÇBÑ!‹æ ñB×~3Á&ê@çˆßµ7½&Gp‰"H9ªëÂLŽà?¦sŒï$sØÑèRßù,› LÀ‘žQDÖl$´"ðΊ‹ä%ÕjµúÛßþö·§OŸ> ÿns€/üe‹±F£ÑèïïïW9 ¡e¢ì°QŘýêØK­ºöŸO“íäº2‚Bò|ÕŸƒ2ˆÂ'ክ!£C&“Éܽ{÷n“ÅÁ³¾²þw£^'DÜU²(<žD?ÞÑo¾äÇw½ï#õsšqq„;u# “â“sý6LMMMñòM6wLŽ…ÿsdÇ9d‘¦*ÒG½º Ï÷Y6A˜‚£Ðt®zÆG7E`‡¬Î±°´•«C ÀEÆ <30fv V«ÕLvae¯­¡% - *6=== '4Ùµx‚ sϵK\ן/€D¡±8â#l(>còv€ð ^wg(*à9zÑ\<'ѨÇg¾Dï€wgTQQË=ø®j—‰ÿT4Æõëׯ㿇uc'‘K“¶ùÅ$/ˆ F HWGu’4rL®ŒM;.r6``¤FPž ˆè6Œ‰‰‰ ,O¢+~EEÛ[Âè'Qb—¡ÿ¾ÊvAYÂ׉ÉG'‹X…1&ú<ˆ´•kJ ' $ÚÁá”¶GDçx×ÖÌï’œœœ4 QÃç°ÃLî:÷¡ã * É™8®ëϽö¢¬ºAŽææNáZ­VÝË©º , CJ´Ã‘äðÆÞ¾PJ ïŠb0‘™þX[s×5á8GùHü§ç`ÌÌA^«ÕjAÑUð†°r˜¶ùÅE(Ý„¡8Èf—™±àEt"ôâ":ùLÀó¸ªÌv»Ý’µ?¾ C$ÿ§N:¥sÅ/¼@íÀ£<0¦Ç!úÿ aŽùrÂÙ\SJ宇w¼i#›Û+•JÅĨKÚʵAéÀ˜0a*ðõJŒ™˜•…ßñ»dƒ&[¾0´ð9ì0»O¢ûÐñ9h¼j{äÀ®ëÏÏÿÙ´+žÐÂ"º <—Ëå®^½zÕÕ3\Rp±ÌØú=|Û€qS‚úiX~ðÙä (]ù)—ËeUø.Ïjwc}‡Ð'åŒoRÁ»pQïXU*• öà›:a-6avuÓ³ÔŒ¥o~Á7 ØÜÀ1000-¼“È#d@pŒËÒÉŽò¾•õ+ÌfŸf'#tžÊŒ[ž7JGÿÀ£OAÑ0UPÙØ4¿0&w”ã[°\á¹7,Åb±¸ÿþýü³Ëð|Ÿe»BÇ^‘£€è,y|¨ŽÇÈÆ{·Ûí;vìÛ²:²|pºólÚʵåÕSì“• ÃÄö(ŸxtÂ(xýEŽ€J¥R±¹ À×®]» ¦ qQœw5% õ7 íÔEF¸ÿþýIÊ€¯§ämwö’5‚ÁFôôã‰Üeƒlq“N}úé§Ÿ&©¯‰õÀÝrÛyMwW¶X,OžúÇØ/z!L”MÚÊ C¢®´9 “‰Â|ûúÞÍ92 W&îuÕuh8<3‰ç¸ã®??ËÖív»Ÿþùç.ËfŒ±K—.]‚G2™Lì8Á×Sò¶‡;{q˸ ¼ëóàÝÉ0»Z­V“íMMMMñ]ü¤õ5ù|>ëÖ­[Ühîv»Ýï¾ûî;þÿ…B¡póæÍ›až¡³ó áΠ\ÜúÙ¶t}!Ä4b( x;«ól6S|2'»î•†:e‰Ê “hTu '3‘“[6Lq}7ª²]‚ó˜$BÓ=D$¾+.³ËðfIî–ærÃ(@˜£ãããã2/<¿«V'#íøøøx’ÁdM0L /€’xŽ›±øê“V^¿~ýú·ß~û­«²9¢;µ“ÀwÍ`ÛCÃÙÕÆø|¦+£Öæ2€»yQF1@ãÎÕ¬DzÁÇ‹NŸ>}úïþîïþ:ÇÆÆÆ\%ðƒ;Ï¢\$¦¤i~i4ùùùyþÙda…o3HÂûp`ŸÊ¢`”GœG ž—íÎà W6 uU&–'.ÿ&áÿ"D·lÈn0G¹ý÷U¶kpûš\µˆ;0ªÈEž*W^.F¶Q‹ ÚÇNgêxNÚÊ Ã;øŒ)^$M挩éQ.L:KîM¶Ü»œÏ'ƒæajpeÂ+(':ýWý¹7»Ùl6/]ºtÉeÙ¹¹¹9 »³>à.Çððððo~ó›ß¤!üŸƒwSÞ{ï½÷°eTÞm÷ÀtìG‰,$[NºÉûú‚wñ¹¾c&ÊâãÈH§ÓéìÙ³glÎÖ!ióKÐa¡º¾“Ô÷ÁèDð#ÐÁê;Gß¡WÃç¶“«++¡Íé+4;À=zô£>ú†ÿÛÈ .WtË€)¾Âó}–í|HÇæÂv>Ö#M ª…•›LD޾X.c(@dØÄq6Áä(ÀW_}õ•é¤ï‘å$aׂϪ …Â?þã?þ£éýçqGý¹7;ì5\:ˆÎ=zôhRÂ5¡¿eË–-<˜†ðöö‹Å"4Ð\E1èw¢|nÚQ†;™:$¢{rãº~3(1.Žb³½ÑÆîØ6q¤m~Á»¶:ùp˜¤/n[ïÐ º"N\ïöÃŒùQØœØÁœËårÿöoÿöop1l#ÿxc P(]ö»Ï²}ƒu«Jˆtœ¨_Ãô™ªT®Ù¼i‚Žý‘%M…òŸ¶rMÛé­ß†Yaë`ìm¥ÑωÓÀ˜|’JËýÁQÕŸ+!\®O|®ì÷q:DÊÃõDïÓ€û&ù‡ïgû^äXOxÕAôý$.ž\êgS€m £¯ÃØI§\.—£vôªÎRÝ öEÎw‘JåÃcÓ¥ãÞgپѵ9‚æŒHо/sœÊÚp£—ëËÀ˜xœ‰ô=žÛív[5¤­\€ßüæ7¿yöìÙ3×ÃX¸ÉX$`QOæq;d“TIQÔŸ·¯Hv};‚¼zq:DuKÓN˜h¥ü»Xü3–l€ÊÉb ”Ï^qØìèë>q_ãæR?›:T¼}eQL¢ï2¦Ö4-xz™8å_GþÂa ‚Ë¥®›ÀǴϲ£ÂfÞ ÒóaÚEõþ½\Ÿ›º®¬¬¬œ8qâ„ÊîO[¹*T€wcìþçþgçÎ;ëõzýôéÓ§]$rqA˜[z|¦Œ±øÎØØà»þÙì/wØÎÏÏÏG-»­V«uûöíÛðoIÉ€ÏÑ3–®óë¢< ŒE#ÿù|>¿°°°Ëårív»=444&±©(Á‘‚ÎýËå¸uëÖ­$èNœó‹H?DTˆænŽ‹y²T*•Lò8T«ÕjÒÏà'…F£Ñxÿý÷ߺ•S¯×ëAÉ]yrUÙ"x. UÞ *×N§sàÀº·â4›Íæ®]»vÙ`i+×–uIK¥R)i Ht+ÀääädZvÀÃ"Z%á.c]|×ޭɳ$Cà•E““““kkî£Hpb“,Õ¾†L·ÛíÂ,¯iàéÓ§OñßVWWW_¼xñÂ×3‹ÅbñÉ“'O\,þ‰tS.—ËP‡˜&­Õj5hHår¹ÜÂÂÂBRœqÏ/¥R©dbh„Kàm9œf³Ùœ›››sQ>wþŽŒŒŒàçpxÄ8n§ž0Sus¿¡Â$Q%O®rÞ´Ûíöààà ÉÍT®¸ÃBu=.¿é'È ”ær‰ÄôìiÒð]UŸŒ^:lŸôÞ¶ÀФ…ë„kÒ>¿A!&0‘\ð¹´u^ÜõÊ@Zü¸õ3AA~Ì@$—džŸ“t7³i¯?±1(‹Å»wïÞÍd2 û'6 ¤Ÿ ‚ ¢7Ù¾}ûöþþþþ¸ëAbs'g’HBý)€ÐA'Û8í’½Dô3AA~PÝzD fbbb"“Édøç4%ÿc,ýõ'‚èUH?ADo’Íf³ðo>|H‰Žë»¯£&íõ'‚èUH?ADï‚çyŠòK(8L3;;;wU¤½þA½ ég‚ ‚è-‚æv>MGR@³Ùl?~üxÜõ°%íõ'‚èUH?á›k™M-A˜S­V«SSSSðoäH8ãããã{öìÙ“Ö3i¯?AD¯Bú™ ‚ z“f³Ùܼyóf¼ø'‚ ‚ ‚EAAAAAAAñ“Íf³‹‹‹‹kkëïʶýAAAA ¥R©Txè¹hqÿžn¼–LÔv¸}éjR‚ zÑy)ºŽ‰ ‚ "ZŠÅbquuuUçl:Ý©mt²@'\üSÛѳ@%H AAD¼èÞ«M»ÓöàHÚù'bC'f#VVVVòù|>îºAAl$à"BQšáÁ ~« ¢' J CÐâV„øˆE&ô¢ ØÆëŽC&ã–Y":ðN%Ä’ƒÇ‰­ƒFŽ‘>&"­ÐX‚“Íf³/^¼ùƒñÀõÜø##Ä Ø¶²Å÷<'Á Æ EŠLè àîF˜1ŒwIH>6xaKÜÛàùÔV¯»6˜iž'‚ ˆäQ.—Ë‘ÛÓ"#Cdhðˆ¤ü®CŠð}Ùû›bÐ`–í¸Ú8dájÛsUQùªú'ÉøT 3T9!\Ÿ‡Ãï`[¾¬-låCÕ¶Ip†A‚vt]íøº@¤]¶§+yR¡JΕ$ý —³-L{§už—Õ[T÷$૾ªr}A|ÙQQFï¨lR“rtH‹iö—-*¹K’uG¤Qµª…¯ˆï{PMgů©Rñ5q¹ŒPõ• ›IÖ‡‘¯;Ar‚ŽL„E¥LÚÚD‘˜”ëBèìú™–³ºººzäÈ‘#¶I|t“.­­ ]]qqôÁÖ`òîaÇn‘àýjª# »\,šê8,Ïa #Hi‰ŒqQ½Dm »SdŠî˜ˆÓÔ*tŒRQ!.Ë *¥¤8\èJÆÜß›írž—é_¶Ò#®uDXÀÚšZ†|è4ÆÄó…¬EuH»€1ÿQ.õŽ ¬ƒm®åÁ7>êv®wÙIßý‡õãc ÷ ¬s¯8döŽhLëF^û†·ýÊÊÊÊ… .D&O°ÓEƒÂW˜§ì²ŽbìíIHU\fÜ,ªDwB¨T*ÃR¤XMt< Âþ^·?²ÙlöÆ7\õ›¨ø]LÛKT¦¨DF³jrŠ Ÿf¾£¨&‹“q©;Áš‰¢qå9T]cÚE¾ÕâÇG¤­œá>ÔÕ×¼/“à€rf¾ Šª±‰ºq9Ï«°AsƒÎ˜6Ñ6 ß 5pp]_YtÆ+ÔÅq9\â{QâRï¨ëð!>ñU_Ó 3Ÿè®[âÖ·%–CWóf’Àº*h¬¹^gØÀõÐìììl\c6L&%SC?ê3d:ˆ ß‹lj XoÓÉ+.ã!¨aOŽQk.k2Á¨½)¢‰åCÖf"g‰‹1l3ÎqÛ†q˜b:DÆ”JLv<]éDY„“éom~_,‹}ôÑGðsÔøÌ0†i³Û,jÃSå0•eüŽ6í$?¦ŽÈQèbQ­#ïaÃAË?¶®&uöÍœ¡‹k½£"¬À‡<øÄg}ḌÒqq±Aã{½:›|Iƒ·;ì'®›ÒØFèzÑl„3) ϸ±5€dd³ÙìÎ;wê~7lè›+Dg¡ƒ¾££<‚Þ1Œc{~ߤ‚øàƒ>pQŽâ1¬Óg6måDa£—LÛ¾lSï¸ ;wîÜi"§®õC“wv«ñ© nßv»Ý†ŸUã/È€åÓ…þ 3ÿÍ>êëkÎL£À׳£¶KÂÛ–|>Ÿß»wï^ø·/¿üòË ß=}úô©é³pûîÞ½{÷Ž;vðÏív»½°°° S¬ãŽ;vìÞ½{·ÎïLú(ˆï¿ÿþ{å`VWWW_¼xñBöÿù|>ùòåË>žÍØÛýÿî»ï¾ëëYaiµZ­sçÎãŸ3™LfbbbBöýR©Têû™Z­Vã¯ÕjµjµZ…åܺuë–ËÝ‹gÏž= ÒÍœ¤è‡°˜¼³k|O]Úív{qqq‘>|øðaÛ²ào»Ýn÷Þ½{÷‚~Óét:>|È?g2™Ìèèè¨m XGìÝ»w/+>ê‹ç‹çÏŸ?üøñã0ï‘fææææšÍf“6™e$Uïd³ÙìÅ‹/¿¥M|×·ÓétΞ={¶ÛívcìòåË—£Ü}7Y·Ä^/m††††r¹\ŽNúXáë’n·ÛžžžæoµZ­Û·oßfŒ±‰‰‰ —Ž˜·ª>Ÿž”S§N‚õèÑ£G­V«%ú.6Îuf­V«õèÑ£Güs.—Ë É¾ï«âj_¨òèx¦Þ{ï½÷àgE·°¬5›ÍæÜÜÜ\Ï€ÊøåË—/_¿~ýZç·ËËËË|¢3qÜ„í·¨Ë…ðŸ¥¥¥%ØgI"ŠvÀ`‡&^Œè2555U¯×ëüs.—Ë]½zõªi9aBÁ9x±—~ƒ‹vH{¹_ýõ×üß¶r‰´AB¶\9óð˜ëïïïß¾}ûö¤Ö7I¸’³N§ÓÆqóS‡$êr¹\~õêÕ«ú§ú§8v”Ód§6Æõëׯ3f?Ùb²n‘áÛ~€úÄÄÞŒ_ódyyy9.Ǽ\–DÈk×®]k·ÛíB¡P8xðàAç·Í¨éB9™„j„ )Ò ÕñÕq¶/Ä&,F·î¾úÍ%&ç‚lB»‚ÞS'Œ]†iýæ«\Ts·AuÓ@mò2¸h‡0!­.òÈÐÕ;ºå½›¯s”QpÕi+WÔ¾;wîÜ u îQ>ؾ.û þ6LX¨J·ûªoÐ1 [¢:àC~]ÚqœßVÕ_dŸ¨Æ…ëúº¶S}×—ÇQ€°!æ¾ì3n]{ÓFŸ‰ÆzÐó|ÍoŒ…»©c£Âþ&(ß…Ë\¡LNNN†­ÕP…`mÚ´iÓ–-[¶ðÏIñ´»h‡(Ê5 ‹áB9666¦Sv&“Éúè#+g\€™ìä†ñ®ˆÊ òv…ñ´ëüÖW;ÄÕ¾ª÷W-°±Kö}Q}Eï'k{¼› ÃÕâI'ùŸêû&׊ÚÁe€H~]÷›¯rU^Ò0;<6»â¸OT[×í&@WêŽ1ŒJ6EºDÔn¢g‹êf\¸h#:ž|×í¶reí ÿ.¿ªÝÁ0»7*ýf×Nõ[_õ5ÕÓºøŽð%¿²úÛ¶‰/½£BÔßív»ÔV¢ßº’Žk;Õw}1QÝ `k£ø²Ïdàqè#À&™°oýÀ˜›äŒQDèìþsDI!¼½Œ×K³³³³&?r•AÚTû6˜}µC\í˘S/¤‚Ú9èÊ<‘"2]˜„=!R¾A}"j;Q»é:W¢_—ýæ«\•’ ³h3uˆ"OT¿qÝ.åAw*Ó%º:GwLpDãe_™”}:|´CÚÊUµ/+::SåsíéÚÚ/c:ÌÂQå(ôU_QÙ¢ç›âÓàK~1.tFÜeö˜N[‰êìB8>ìTŸõÅà¶õuÀÖqäË>s]OÛÍ}š&ý…€·¡Nßò~ѱá¼ë2“ÝQAŸ—;¨®PØvpU®ÈØ—¡jg›v êK‘ÑÀ‰îÂEG>T`…`ÒÿªD-• #ƒA;Õ>úÍW¹*%fQ¬{®Î´ß|µC˜wµY •¯!c1¡û›¨aÀuöÑi+We(ÂßêèxW jÆäŽ@[@Ñ꫾²g‹pÝ"#Hgù’_—ïÀI’ Œ,†•‡0Ù©Q×ê•(œ kkvùw\Ùg*ð¸²Íí#êÛMʨôC°uûU`º>• ìÕYøúÙØU!@ø<’+|]!æº\~Ž_†™˜˜˜€çrnß¾};(³)Î΢úLr¡P(är¹\½^¯Ã3nœF£ÑضmÛ6x寛Í$‘Íf³ÃÃÃÃðo:ïÅ™šššêëëëƒ×¦aºÝnwddd¤¿¿¿_vìñãÇŸ?þœ69t¥–¯~‹BTø>ÃÛn·Ûƒƒƒƒª~c,þvf%gìmýÚn·Û‡:¤:›<=== ó^ˆ²¸ãúë´¾¾ Ë}3Ÿûh‡4–«óÛ\.—;uêÔ)ß1–ºD%gðz,þc,9;þüy,Kõz½4ÿp’&AvjÔõõ}+€íº%ûß¼åŠr¹\ž™™™áŸ»Ýn÷Ã?üPG~£ž‡’ —%“µ/·ÑD¶¾ÚylllLåX å€×’1ö&ŽÉâ Âôôôt˜kÊåõ‰ÃÆÆÆÆ¢ðè†m‡¨Ê… VæmÊç×_gdr¯©éÕdÍf³yüøñã²ÿït:C‡‚eÚÞûk’ü=zP¾0|øPwíÛh4óóóó2»xjjj ޳B¡P¸ÿþ}Ñ8 } €-xAå]ì½J£Ñhô÷÷÷÷!6oÞ¼{_gfffDá!a²â¾~ýúõË—/_òÏxG££8[­VëÑ£GøçL&“Õ©{_u³ó‹Åâ“'OžÀl¯õz½ŽÛxppp*éÉÉÉIÙä…wYƒ&¨|>Ÿ¿uëÖ-U¶T_ý¥©Õjµ Ù„„ÍœmJÔrÆaþÙ·³Ø–J¥RYZZZ‚v„ ’.˜¨êëëVÛuK\öƒë9¶R©TlÿŒùÕ8ä;Ú&'''ez>Žˆ)¾ûoâ âð5„ÌáY«Õj####\þeãHêÐ9K‹'G“P]¼ »ûÏØ›—®×ëuþ¹^¯×§¦¦¦øg<uðÕ¾Û½¯XéŽáÅŽËëŒT´Ûíö‚Îwqˆ™éN^ 1¦ïµ… oâ/ºÚ¤Õjµ¶nݺÊ_¡P(ܼyó&þ.6Z{㑳â†C³ÙlÊ&M_ý•<`¦¦¦¦`[2&o£°ÏÁ2QäP\í` Öua&Z8ÞÂ2ª¢$_Ñ#ÂW;¤­Ü  ® s][R¨V«Õ={öìÁc?jùåó޳*—Ëåîܹs'ŠH€8ä ;ÔMìßý&Úõþ3.ŸãK|Ù©QÈ/Ô=®ŽØ®[Òf?ˆ¸|ùòe(Ǧ‹Æâ›‡’$19ŠÌᑚ*‡g£Ñh|øá‡ò6Ëår¹'Ož0ß|_«®óáêÕ«Wáï®_¿~=HÙ?~ü8^LŠv®ñ÷[DƒÃ½­ív»ý¯ÿú¯ÿ*Sž¾ú-*yQ*•J8d·‘Ë£=x·€±·CIãl]T‹ü(Ç›.¶Ml<öõõõagQšð•ã")åB®nøj˜…3 wüöõõõÁ W¸¨/pç‡S( <®¦Ñ¢+gØ¡îë]MõtÞó¿ÿÍßüÍßøÖ¯aä!;Õ—üž9sæ w,„= fÝ’ûAE¡P(üþ÷¿ÿ=ü[&“ÉܺuëV\Ñ$¾sCù„Ë’Íî?cësC¨Ž=5Æûï¿ÿ>¸Ïþ¿ R©TDán>À^4“ó¾S_íeûª€ R‹nW÷[¨ÉÀ‡çl8=zäq³=³% ¡ñçQ: —v»Ý2õöµZ­&:¢:baƒ(”4éIfl"šl€ã ïd¸" ÇLðÕi+W‡0É9aä¡ÛívqÞø|q/C79[õÕ}_ÉGá\—œá(ÝdÁ¾ô>'ÍÍF™7‚c"I°S]Êo«Õj;wîÿæ(@˜uKRÀòÓÈ Ÿú'ê–ï¬V«U™Ž%"÷ —%“ Ï ÆV}‡±·“çqT &óÔ½h¦œÉßd1†ã«¢l_p✤‡Ò„¹½A”üO'áN˜P%“öå+2GÀøøø8WN½Ffw–`…®JnbÞuL[’™0“‰.¾°i ’^®6ÉêaΙÚâ²¾.åQ‰bLÅ%g¶Q¾ôÌÆ]­V«Q/00:ò$;Õ¥üÂy\góGDØuK¯Pÿø7“ÈŠ8ç¡$Á–aòâœ:uê?,‹DÁ9̺Ýn÷رcÇøxG´CÊ%ùw5½ &!6è¡Áï«¢n_TNlÈ)ÿ ¸Ç;Ì">ÌN'–7ÝäaÛ6N*xtEÔ~Œ©å×W¿ù*׆©©©)ll …ÂùóçÏÛ– Qí%©8Q9„|9`¹a»i'Šö»\Ód€.fq8ÆÂÔוÃ;¢Ó6¦Lû ç Ò‰ð©wøŽd\vF%I´S]Èo±X,žº;¤Œ™‡ÈòþÙv@Ú„êŠÎtÆóÕQ¶¯.ªu<@]‰Ãa Q |Qò?Û°-‡…³Ù8 Ë‘¯~óUn𽸾v¶T ¨¸Û!HŸñ,±üs˜H)ؾ"°{Ûáë̲ |µCÚÊ5Á$ ^˜™ÔïÖE‘¡ße}]9©Uó–‡0$EÎ`b,ŽŽI“Þ ƒJÒf§êÀ¯YcìÍèÒ¥K—lÊq±nI‚ý6·çcL/3 óotåÖ6 Fëˆd_ÕØl6›8 šS¤×êTbxxxX÷ûØ‹†w0MÀ¡A:;€x1èÊËnÚq—ËØÛ<… ï” Øo&žvíÔ0õÍçóùË—/_fÌ,šATŽ‹uKÒ숮jžœœœTåˆ[?„EGOšDÄð9C÷vÆ~ql‰64qz½^][ËyË »Ð9þüyx5EØ‹6???ošY—ƒw·tB½ðb0èþN_íà«\]Dañª8—·à~Ó9£‡w:u0Þ1×Mþ'««nöjÑYº°;:ðcòH_ýæ«\W¸83+x‚IJ;@ã†1¹lã-Õu1¸k˜øÊL_åâÄOI”5HÚÚ×W¹ºà(°}ûöíSJ×®]»f6õ5ÙÉ ^Hº¨o6›ÍÎÍÍÍ™f.×™‡|9Xâ”3 £C¢Ö;xĘþ.ºOy€¸²S£ª/®7¬5\®[â¶|$C…×,r‚nZˆ{ ƒŽž4‰ âK›õEP$JµZ­–J¥’²À|>Ÿ_YYY÷Nݱ].—Ë¢û*WVVVD™Íf³‹‹‹‹ü{««««a½_¸²g3ö©P+  IDAT&$~Wt}˜¯vðUn¹\.ÏÎÎΚ´™¨.²v›…ß3}Öììì¬è=ñ»©ÊÅrcR>7-cmmm-(Û)þLÖ‹ÅbñÂ… ‚êïäÕ‘_ýæº\,‹ªñ‹Qõ-®§nvZü»¨ÆArj"¦ãGWWù*צlެßq{™è M[ûú(×´}Eò+k_ÆÌô¸¨¾xüã︰E|Öö™‰®ÁãAôž¸/tÚBçý|êlúØµÞ ÂÄ~•ÕÓ¥<ø²S}ʯ (×&vÆÇºÅ—}&×ßdî3Ñë"9P}?*ý€u•N=[%¢¶’þYåß••Éÿ_W1ÆÄ ÅÞn·ÛPHd/`²è3&Ñ‹ë|Çw;ø(×dá pAm!üµ5³Å§®*’‘¡¨«€Eϰü²vɯi{Á÷Óm'Ù³ÃÔƒ1½~s]®lq¥R`²6åšd2#&ªq¡;Žeõ ’Ñ87Qùª )L¹A†ˆ¬E}"[PFáðÙi)צ}Es²¬ºNkQ}uô´k@˜úê.¦M е5¹~õ…Éü&Ó“>õƒØæÒ±9]ê›gé¼·Oyða§ú–ß ö1ZÔÅźŗ}¦ó,_ÆÄ²£ªgúÁ—`mM,â6€ÏÎf³Ù‹/^ÄÏ€2TG. 2Yüè£>2žÏT D++++'Nœ8äðáE SoUûjåÊ¥ AÊQ&üºÈÚÚ¶\ßÞu×m š0T;a2t_ÒúMTn@Ü&²Å…m=}·C˜q¬kØ<ÃÄíºÜ°m ŸáÛÆöuY®MûšFºÔ•¾¶õ•½»í€1²ÓÅv‡XE˜¹¢;vƒ~ç³îºíeÔ§<ø°S}˯ªMÃ8’|®[|ÙgAÏ1iGS½nêèaÌ­~p±‚ðµnCìì’ñÍ7ß|#ú»¬¸ó@gÓÅÇœe4P¹0`!Ññà¸ðîbt~¤¯vp]n7¨Tž-®œ-¦6ÌJ§Ì ‚dÍÔH´ñ^úè7Wåºpˆú5ŒÀTÎ\´ƒÍ8¶™Lä-ÌN«r9¦ý6Db²ˆH[ûº*צ}eº_Õ¾&F³I8©cʰ¾:ó±­^Óuš›è2“6ó­LÞÉd «wtÑÑÿ¢öö%¾ì_ßòËØú1v\G±nñeŸqp¿˜È¨^×ÝÕú U¢rÌÎÎÎ=«R©Tdú_67ÁïËd>h÷ß *€; hà‹Ê ’c[o—áß6íà³\…æ\¨>"L ªú»Z踔9•Ò¶©¯ëòDøè·°å-®TƒI¸œaj˜v0™¸\ȱª®aŒ_årTí¤'Q9dÏKzû†-×¶}EzO§}U k›0o_úrLÇuÐÂ!Ì.ºjœ…)×·~a±Õ;¦¨æ­ çø_ö¯¯úârÃÌéQ®[‹Æ>3•U[½n²V?Déý}míí>2Õ?\~Eïëu÷ßaŒ/‚ ‚ ˆ^Bd¬ú\`ld\œNpá±Þ—1w‰ÿpYi^·@=ãêhÍF#Ì1Š0ÏÂów¦Jm3¤AAô""'ÀFY¬E‰éît¯À©Ú1 \k„]gôʺÅUDÄF&Jcâ(㉣W¼hAA.1ÍœMØa›E=ÍÀEC¯/þ\%þc,ë(ß°¯]:E6*Q;D¡þª£‰¥W¼hAA®N€ÕÕÕÕO>ù䓸ëÔklÔ(nƒ÷r€ËÄŒ¥oÝ‚#‰xÀvIÕÂ1aDí`l}²¿TîþAAj*•J¥×wi‰èÙè@Û‘QQùgz½ï£ ŒøöÛo¿%'AAAÿŸb±XüãÿøGø·Îeâ¿´tƒí‡#cóØAAA-{uñ`s寈´ï®Š®¤£ˆ"7ÄåØ<‚ ‚ ‚ ,P9zu8È鱑„?ârd³ÙìââââÚš_Þ¯|LAA„F£¿¿¿?îzá†N§Ó™žžž¾|ùòåk×®]‹»>AAAAx"›Íf/^¼x1îzAAAы݅ ÏEôj‚Ÿà»;EçJxÐù$‚ ‚ ‚  ²Ìšð>I¸€5I2¢³ðuNÞ&“¢Ë,›¸ DeÂç‘€ ìÀ‰‚LǾc—²±ÚÚÈæ ¨óèîb9¸-}Ê$–ÿ8Šá±Œß;èÿ ‚ ¢—Ás¶Ñ:Õ4³¦¡†Ó¾" ##Ì"\Óº«Ê’AaNØì­2G9äì:.&a_ÑÂMWó› (ÿIpÈ€ ‚ ä„rÀsMgÇÚ8Yû0.à{„5Ž ¡U©T*:»Y2à{ó¶S9Ȉ!;ภ³øçúÃ¥NÙ¨ˆt'íüÛÛÒe4^L'á*1rA„œPÕ¹s×áû°<—“µËracÂxh|™­ÐHQåU † ì ³X‡cÿ–Õ}°½†è8U’ši"Œ3Zô 9‚ BN(@ÔˆvÚ”€°¬›( _dl*°ó,ªdF1T(iØ¡ £StBÑeNABÑQ3jK;\GPàˆ7Wõ$zh©t¯î÷"턵ÁÒfqâ²y} Ú‚ˆ"è Mvîܹ3¬€¸(ƒóñǬûÝ|>Ÿqñ\¢7ÁoÏÞɳQÀ.“_FÅ|ðíoMõG˜g„k²ÙlvçÎ;mOóî@R-VDG! ¢×kƒ¥Ñþb,z[7 ‚6„áÿ÷ÂûzC¦á¨Üõ%LЇŠ+‹E½¢>ûîäÙzŽe7‚˜Ö;(,ÉUØR’e^Ö–kkéöŽ›’ä>òÍF~w¶»ŠQ3 JésTÝúâãÝu“:§iç–pGXÌ…ý•íÅÁcÂÔ¶¿OZZO&1–eþ×2“‰HµàŘ!•»Ó›âöVùp¸¾¾Ê¥ÑëæKY¸¨/¥?räÈÛã5¾'¡02o2Uˆ&:ü^A aê÷ÄäC/Ù´‡èªÕ0è8I“OebYwa¼EÙGi"É“›Ž\÷ 9ĸGkkñÛ‹i!¬ æÊþŠÚÖ¾M²@vÄ\æð…‹ñÌûùÙC>|ØU…‹ÅbñÉ“'Or¹\Nçûccccº†•û •J¥òàÁƒ™L&£ó}þŒ¤ ´^æÒ¥K—šÍf“1Ær¹\îêÕ«W]–ŸÏçó—/_¾Ì?W«Õj­V«™”‘Íf³Œ1Öív»gÏž=û§?ýéOׯ_¿î«Þ¶$Uægggg''''M~“Éd2eÞãããã¸^ƒƒƒƒØXžœœœì•pp×}¤³ ÆåÇÕ÷i“O‚pA«ÕjmݺukµZ­Šþ¿Z­V·nݺ5I‹:NgÏž={ðXÞ¼yóf>O°6XZì/^ߨœ|—ßÖÆ©üL˜úÚû`llllfff†±_5©shªÂƒY' ‡˜¨v´E¡ 2!¡r/Ѓ¾ÔÏ>íŽW!‚.îªÊxª›Õ7®d5W}¡ºRU–YÑxˆ"„×%®õ’¨<¸]D z|õ»ëw÷QOeFq îPê¤ä#qõøh@Ür ßݦ.”àLŸ°6XZì/ ƒ’ÕMMª#aoy  ª‡ËùÎ8oD Í¡r XÐDd£tu® råeku:ú}P¨²k€Î–0“f¸Ü°ÊÜŘºW[eDõK™÷µÈøì³Ï>3éÑyRܶi2à]ë%“soq;’.Ÿäؘ¤IØ@1䈆°6XZì/U}tìØ¿ýÛ¿ý[™­#rˆl£Ï>ûì3Óo\9[¿ÐN’5u¼uàåË—/»Ýn—±7áçÊç¥IæMY^^^æÎWÆÛ²eË–M›6mгN6øè#—Ii}ÒËòIAˆ‰Ã‹ÚþrYž+dddd¤Ûív3™Læ‹/¾ø‚/ö'†±7 ö6oÞ¼¹T*•\Ö}£#Løõ×_Íÿm› 0ŸÏç÷îÝ»—^]]]}ñâÅ ß>}úô)üüî»ï¾Kå®/—1ÆvïÞ½{ÇŽ;øççÏŸ?üøñc²£fÊäpOü¢äXõz½W‰d’™·áÅ‹/VWWWã®GX\÷Q6›Í ¸©_zY> }dGV’B-»ÒÎæhšw—•éâ´Ï²]’†zŠî®ORˆ4á«0ƒ"xF£¿¿¿_õÖív»####:‘2¸LòÈã\.—[ZZZ²9ãâ}“„ÐP¯×ëðª²S§N2-xûöíÛûûûûùç—/_¾|ýúõkß.,,,ÀÌØ\ÁQ¹¿(úÑÑÑQ˜ ûáÇÃx eI¶ðÎýØØØ˜j@…¯4áT«ÕªŽ'‡Ùà,ªAƒç4˜™™™‘}—%¢‰KöþªòTIMtÛ,ˆ\¦k™OXWDE±X,~ûí·ßÚ$,ý®—û(ˆüîiÁ‡^‚ÌÎÎÎÊŽ¹ñ¹³\.—UgÖ£¨'‡ÙB …BáÕ«W¯tï®Þ]·L^¿0ŃʎҠ—å*HZ=1<ÞèaìÍbiiiivvv¶òzØä“Pý&éå‰àãW¦7 ““““|Ü«¾‡7<Ãu§H&[Ì8¨naß×öæ[ÛL¡‡ŸÛœE€†Ðòòò²­!Ca©Ü_B‚qDÀÿkïþaãªöÄ_?mi‹Â)pÒÀ Þ«Ú¼ŸŠ-¥v7RÆ‘Vl$R€äA"ʉ„½r "¡G”§·ÉŽR€Qvå-ƒ“—ŠhbSàÅ}~EÞ‡óî¹3sïüñ|>UìŒïœsÿŸï9ç{ʼ€Ê÷ˆíííí]»víÚ0ËÄø‡s¾Wñ½¢›`a¯êõzýË/¿üòüã¿úꫯŠÞçÛýÝa>FLrÝyÜXÍ{á ­­­­ýë¿þë¿¢Lí\¾|ùr§$ÀIò¸ñÞ)PvÝÓ—øªögѺ/--- s䯍—3k¹Ó,óóóó—/_¾Üë÷ÌÎÎÎ~úé§Ÿ¶{FÅŸ)’äzvvvöÒ¥K—z-פ)²o7ÒÆuÖá´‘ÞÍ(—z½^ÿñÇìÔP­­­­µ[Á­Ÿú¦×ÇÒÒÒR7A€^ßͺ‘H’$¹yóæÍt>j^ºvž{î¹çz-Ô/¿üòËO?ýôSúóÌÌÌÌÓO?ý´íþºÝxXìÁÁÁÁƒôú]ƒ2ˆF ‡Ó¸žóEÅóÜ;õÏÌÌÌüïÿþïÿöÚ+8;;;ûÑG}”>ØNœ8q¢Èƒ&}0…wñâÅ‹é6Ë>FûûûûãpœûùI{ËËËËySÝRé”·$I’û·û·²^¢{1777÷Ö[o½•$ó¦àÍÏÏÏç½ÀVQ÷p=ôÔÊÊÊJû³—º—µúO7F½œF£7°ÓùÚiÃdoo½õÖ[½$s­Õjµ{÷îÝ{ñÅ_ÌûÌììììW_}õUü™"‘ùùùùQšJ1ª²‚=é0ý© í’Ù¦£ÒÆõÁÁÁÁ?ÿó?ÿs:ú|åï’¤øH¤øÝ$,_z†×Q§iÆeÔwwww7½G…A€¬øÒk7®Çêêêj# s[[[[·nݺ•$£0ñ0în}÷Ýwßõú·ÓÓÓÓys@'u»O<ñÄO>ùä“éÿu“[ O˜]4´¸¸¸~.~°ÇÚeÅ,#±Y˜-µÈE¸¸¸¸˜÷Ùt*B˜Õ³SýÛm/ž«”WÖðÅ¥¬úömVqΊ8WH‘d¡ÓÓÓÓáþ¥Ã.Ûö÷÷÷_{íµ×ÂÕF:²°++++é4ªŽÑ_ÿú׿ö»ªæóó0©â^W«ÕjgΜ9þnqqq1žê¶µµµõüóÏ?Níd9³ìììì?~üxüÌ^XXXˆŸ¥gΜ9ßOª¨{½^¯Ÿ?þ|¼Íx:`7ÛÌÒMݯ\¹reXsÙG±œyÓ;ãw 4Ù[¯9êõzýîÝ»wÛÒA^¯o‘þ‰'NÜ»wïž|Ù²‚=++++3333[[[[YÓl6›SS¿}'OGö„SZò¶Ól6›GŽ9’¾£¤Óˆ²‘q‡F’øàƒªËTÄÁÁÁÁûï¿ÿ~^ïÒåË—/‡÷†¬œPUÔ=Î¥QÅþì¥îÃxwÕrž:uêTØàÞÙÙÙi7ÄÿÝwß}·Û{x£Ñht~]$@$ÅøsssswïÞ½;ny ª–쉟ýí4›Ífzý†#{ÒÑ"í¶“BÃÀçùóçÏÇÇ(1´³³³sîܹsÝ–/IÊ­o’<î\;‹èæ;{Ñ6®Eßm2Àx.dYl7Û¨­oµZ­ëׯ_·¶¶¶6N™2M£zÎw+^%£ÓÒqíFà„ÑòÐÇüqÞKÍþþþþo¼ñFørÏù­ÕjµííííøE¿S2ϲŽÑ(5šŠ:,ç'íÅK?&Iç+á»Õ0u 4îïïïÇ÷¢0'Tuïe)Í^ög‘º‡¹°’d8Ë‘Žj9»²ÖjµZ·oß¾]tûF£Î ßÙÙÙyíµ×^ ¿#ìííí½ñÆo¤ŸiµZ­³gÏžMŽY üøù—‡1åcTÅÁž½½½½wß}÷Ý^¶•¬»Éî®ðÉ'Ÿ|^Y÷¡~†Í—YßÔúúúz82 žÒ¿»uÀèUÛ@ÉáÚµk×âÈo»a00)jµZíÊ•+WÂß]¸páBÖ _:”²]£;+Zž$_j>úè£òîß­V«uòäÉ“YA€^ÿe‹ 0 ²Ýnooo·û›¬FÛ0™Z¾ §ñUQ÷x›E¦Òô2 ³HÝÃ\XI2Ø‘°©Q,gV¾“NSÖ’¤øT®åååå°ñ¿¹¹¹7ãÏäM“ˆ§‰d5ð[­VëøñãÇÃ˜Žª_Ź̮_¿~=ošo'é;J¯ÃÚ›þ.¾gììììܸqãF/ÛO’rë› §5mnnnÆS¥Óýæ=8uêÔ©~¾³“¶€$é? dÝ\céPåtè¦è+ÃÐ.ÁKžvsÂÚ™ýâ‹/¾(:|¶[[[[§OŸ>¾vz äâå§†ÑøO5›ÍfÙs “d°Ç}ÔÊ9Éu/C¯«w {…ˆ¢I*Û%#®¢î½n³›£ZÎ^ó ×é’uÛø¯â¥Có¦;ŒŠq??éM?/Ÿã ]’ã2ê÷¾ sÆÁQ5èrÆùNŠiâ@E(kÙÇ••••ðySä3y²øY½üqbÅ"K`fq°gW·‰§Z÷“l½ªú¦e¼ÿþý;wîÜÉû\8šéرcǪuß1$½%ìçÄËl÷µ»‘޲ôå=í¡é4¤×0,RãzÎg‰—ÍÊšßX†^††æŠ4þyŒÂ,éáRSÃr˜ÎOŠ‹¬EŸùEzC«T´×¶ÝðúAÔ½ŠmöºBG»àGÆ¥œeˆâY«>ĉ޲>ÓIÜÀ_ZZZŠ;ššÍf3x}¬ÊÕmÂgz?ÉïÊ¡p˜Wó)è'`ªŸdxí"0¶ûØ ‡¦•%m ¤ 1²FdÝ a\Ïùxy™8qQ™zš5ç?Izë ×cT†I®;T­]Gé(I™¦Ò‡KBW=ú©P —d€ýD`ÚÍ/²ÝÇÛ‡|cÄ7K:B ^3ö½÷Þ{OÊÉvÎùz½^ÿøã?N>8888{öìÙ~Ìäévhh^ã?Õ)pŽQ¯&¹îüjP+ÿŒ¢I®;ý9wîܹNIøâ%{!çÈZg}yyy9Î1Pårlô®ªeØË–vvvšJ.«ÚM^“^ $I÷ÉãáXýÌeçÙîãífeÒ‡ã³xÍX (÷s¾^¯×¿üòË/Ãd5yÿ‡!/Û<¼¾]`ÜQ?&¹î“¬× œìnЊŽPi×ÃVEÝãm *t³?{3èùÏãRÎ2IÂ×O¢¾¬üYSŠ$!œdã0²­ÌçnYõ s2ÍÏÏÏïîîî†Ó1Óó3=÷ÊJÝNá@·Éã¡§Ý$DŠŒ„à l÷×á ñƒò0-Ó¨7,ãzÎ×jµÚçŸþyø‚šÕóP¶xQ»òå-õ×n‰À¬më1*Ã$×ǪNÜ4lí—UÔ}˜û³èýsØ]Î^ßkãwÚ,E’ðe}æÛo¿ý6ï<©Õjµ{÷îÝ‹ó„Ïß~ fãð.ç ég$RUõ§~Æ+ŸýüóÏ?‡çç ’O$IwÉ[­VëöíÛ·ÓŸ»‰¢ÄK:„×v¯Ï{˜zÉÇeXƒ5Žç|Ör++++ƒR¿pe%´ÉjüÇ+´Z­ÖÙ³gφ£™òPãxŒÊ2ÉuŸTñ(¿¢¡ª—xê¤è‹m»©ˆUÔ}û³hÝ‹Ü?«4ªål·4d;EGi4›ÍæâââbúsV?þ̉'NÜ»wï^Ü.©×ëõ»wïÞMŸoér¢aã?/@PÖ3ºHàcT·—5²mØ÷®X/ÉŽóTQßF£ÑX[[[ëöïªN>ÙU NxöìÙ³í>6X§§§§_ýõ×;}G¼ãÁÁÁÁÍ›7oÚî?n·Õjµ®_¿~=ÜöaY¦±Ý°Ã4Rû(GeK’_—Ì2ÉË»Œ£q;çgggg¿úꫯ‹AÎ'Œƒˆ·oß¾æÈküg­H°µµµuúôéÓq ~@Û1*Ó$×}RݹsçÎýû÷ï§?ÏÍÍÍøàƒðwkkkkE^8Fãûï¿ÿ>/ãw*’×í°Ö¥¥¥¥‡>l÷‹]½zõj^¯“mç|6666Âù„×í4F7£U²rÃFþÒ IDATììììܸqãFús<¬¿hùâ ÀÎÎÎÎåË—/‡Ÿ—cT…I®û¤ê&è“õ|–éééé>ú裼w‘xÝõ¬@|uß3«ØŸeÔ=OÜ8.Òé3Œrö#n$8qâÄÅ‹/æ}>~§+ªH¿ÕjµŽ?~<,OhsssóÅ_|±Ý³­H€ I²›íhññ·í¥Û\]]] ×ÍðôÆßùl¯â":µ³BËËËËaùʬoš¯¢ÛiŸé˜"çnÏêõzýáÇÓ!Ñíi¤6666²†SÇY “äñÍywww·ÓçF£›áÙ¶û[ËËËËEŽGÖwt:©ó†Ü¹ ãó+þ¾ôÿÓ²f}W·Áƒx_ô{ã‰÷W7²x¿ä+ÛÌWÆ9_—ÝžkEËÖï~+zæ]«íêU¯×ëÿó?ÿó?ÝF˜‹ü]•÷¥Pxãí‡ÛMï¥U÷ШžŸUl3®kCqŒBýÞ—²Žwüì¿cooo¯Ûw¯~Ê™õÒnYïyy÷*êžµÍø¼Êz¿ÈÚf•u/º¯‹ß*Ên³—k(ëþ™õyõlW§nÊ3;;;ûé§Ÿ~ÚnÆŸ)2šjvvvöÒ¥K—Š”!•uÞµ;>ñùvÔ·—ʺ&ÛÃðóU’$ÿ~Ðh4éyÖ-¼~²Ê7êõÅ÷ƒŽeè%uÑ·;iÚݤó)‡íþVÖÉZD‘—µ¼cž%<z©s¨—Ö¨z=YÒïœäm–¹óX½ ÏÓ*¶ÙÍõ×n;ÃPå})Õo ª};)çgY×{xìF¹œeß—ò:G²ÊtéÒ¥Kyï^U”3ÞŸ»»»»ùË_þRt;:"ʪ{ªS®ŸØ§Ÿ~úiÞ6«®{–¢÷ìv×få¬2$Ý«ùË_þ2È ^•º9ßß~ûí·ããZ$˜3JÛKõzoTƒ¸×6G7ÁÍaÖ·Œçbzw= IþqèO'Eç褊y°Ýßj6›ÍNó¥²¾£È°”8!KQÝÖ9Td>“­Êsþ0ØÜÜÜœ™™™æ54ÉÇh’ë>‰Â5Êó\¸páÂÿ÷ÿ÷ ÊÔÎÅ‹/†Ë©å)²ZÙuÏZó=ÏââââŸÿüç?wú\èâÅ‹‹l»×¥àz}gŠU]Î~5›Íf‘shsss³ÝqsîܹsÚA§OŸ>]äù;êÛKõòL¤­­­­§žzê©nÚ¨ñ’¡Q¯o?z dÍ1é¤ÕjµŽ=zô™gžy&¯1¸¹¹¹9555ÕÍMÌv+M,qäÈ‘#í.€4Ûd7ß±¾¾¾>555UäfJëœ&ºèô0ÛÙÙÙ9räÈ‘£G5ïŸNª<çGEzí}¡µºNÂ1Ê3ÉuŸD yÏ÷ôÙ^4Ãø 4›ÍfÞ¹™>‹‹¤ª¨{•û³Ý¶»­{–ô¾]¤?Ìrö«Ùl6óê¹···÷Ì3Ï/¹cšˆ*=îIò¸|uuuu°%-ß$×`ÜüÓ° ÀdK—×v9ª4???ÿèÑ£GYÿwýúõë–<=\–þ®Óç.\¸pá°õ€OrÝÑ“Žv9F‰ Éâââ¢iE“çààààôéÓ§'±<ÉuP!#¿´‡©V«Õ¶···Ãy½½½“'Ož&¹îÃ’.áô(°±±±1Œ²,///·+G§ÿgñqØÝÝÝ-²öv£ÑhÖ}@Ifggg¿ýöÛoeÈZï¹j“ØØØØHëõí·ß~;;;;[ôoãàÁ0Ž5J01ÂFçòòòrh4A–gRa½ºmü§êõzýáÇ=zôèáÇëõz½Š²@?âgyÈó *>€Ãã00èñ$Â!üE‡ýb[P¶ð9¼Ãž_0‚ÂB»‹4¼ÈË: C<Ç»×â¼mf /zÓŸ°×¾¬ýœ÷rEÿ<û(KÌ Ï¥xVx ‡ÿ×îÚ.ú¹QήÛÑÑéÜ Ÿ‰¦²µî«~ß!ªxÏä O¼N±‰ITd^vzÓíôâßèû}9ù÷ÿ÷/úÙZ­V{õÕW_íçûÈöûßÿþ÷UÞ«Þþ$òì£_ñ³!œjÕiO:m«Ý3#<«hÈ–õläp×íhy^?=LJ|ñµSƾ*û=“Cl‡Ó»^³šø&ÄM-ëÅ&ë»Ók¨È¾Ž#´ƒžŸ?ŽÚ ½Þ›Â¼ 1/,“¡Œg_Öy_Óñ5ßéþ•wn¶»WäýMU½oys«¼/‡÷ãvÇ%ΉRUyÂ}Ö;îýoװΫKÑçDÖ¹•jw+óÙÖËùšÊº·wºÿörÝö;--o?wº?ä%è-û¹ßîyÖéØä ñþ …Ç-¯.ãh—P9똷Ûw©võ[™í¬~ß3Ãr¥×Â î« *ÇUÖ3nè£YÊ~qÍ»iæ=ܪÖ.©HQ“°÷@‘ê ÎÉvçbz#èe˜fÞ°Ñ"ŠîŸAﻲËÕíöŠ^óÝÜ_âãYfuãïé´Ÿª.W»K–*de<ûÊ Ù7y÷‹N €²®ùA}O–"I6ãeUçO»`D‘é;Eí¶ÑÍßg“²žmq]ßzë­·ŠN‡êt.å½l÷rÝö(ºŸ»}VŒJ èó0.o¯#:]Ÿe$ÒíÇ Uwõúž™7z©h¶jƒÊ‘4 €ßÅ'Øüüü|™_°¿¿¿¿ºººš$I2===ýÑG}ôÖ[o½uþüùóI’${{{{ï¾ûî»e~'d©Õjµíííí¹¹¹¹a—evvvö•W^y%I’dgggçÈ‘#G¦¦¦¦VVVV’$I––––=zôèçŸþùĉ'’$I¾ùæ›oö÷÷÷;mûòåË—wvvv’$Iææææ®^½zµÊºŒ›^΃ééé鯿þúëN½¤KKKKå”’q7JϾF£ÑX[[[K^\\\œ lnnn&ÉãûÅöööv·/=óóóóý¾¼,///—ýþQT­V«9sæLúóƒdÝkß{ï½÷¦§§§“äññÛÞÞÞ®¢<ï¼óÎ;éýéÖ­[·¶¶¶¶’äñsã½÷Þ{/I’äààààý÷ß?«œûûûûo¼ñÆ{{{{í¾goooï7Þx#ï¹²¹¹¹9•!}N%É?û²žmYuý¯ÿú¯ÿúä“O>I’ö϶NçÒÒÒÒRÖý|×íÊÊÊJ¼Ÿyæ™gÒãwâĉŸ}öÙgñß¼úꫯ†×ð¨ÙÛÛÛ{æ™gžiw}üñLJ ¾_~ùå—Ÿ~úé§$I’W^y啬©/¿üòËIRü½h’ÔjµÚ•+W®¤?¯¬¬¬¬¯¯¯—ù½¼g†×t’$Éõëׯ·Z­V’$I«Õj]¿~ýz’<¾îÂÏ J\¾Ÿ~úé§_~ùå—A—c`ºé‘é'bÖÏ0®2ПqûaÇvî¬k¥ßžü¢/ç“0 k[Y÷¡¬Ï凬¡¦†7P~¹Fi@ªŸg_#Šö¬tÓ;–uûy?Èêä³%~6d›AÍ} ÷m¼:%fÍ’÷ÎÓoÀ&/9iY϶^§Ðtêù ËPÖÔƒ~§äiw.´+ï¸LýË;ÆIÒÛ*ã8 ÛcU$_Öû*tûžÙ©w}P#¬Š”¯H}ú1 # 'šé÷‚ÉzaFòˆ^oÄ£ ë„ôMl\eÜxËÔiŽ[?Ãø³¾£Œ`GüB4*/Ý–+<‡;}6ë¼ÉºIi@ô£Ì}_涺 ª\Y†uèõÙ—UÞ¬ýÛéE¢ÝKvÞ÷9ŽñËZ?ǾÌmu«è9ŸŸU½¬å5ò»i JV£³Œg[?ÉøÒ2 våÇQxgí5_Ȩ<›;锈²]‡]Öþ|‹¾g „çñ Ѡߊ÷«ö»V«Õ:zôèÑtXNÙÃDRáðŽ$yùä“éÏUß,bkkkkfff&ïšk6›Íôšë»Ãiú»ªcáõôâ‹/¾–#<‡ªê¨ÉÒ®¶Šœ'ûûûû/¾øâ‹‚ßá³maaa!þÿ08 瘷3ÌwÖ8Wͨ}ÊæÔ~¡ø“w¼ùí5ÑkÇQßÏ­oà½qãÆ4·@’$É›o¾ùfu%,Γ$ÕÎÿá…^xöÙgŸ ·ººº:è\ н8R´³³³óÔSO=•>ˆö÷÷÷ßÿý÷âD*ú¿ÿû¿ÿ“,§áË˰Øp¸¤=ª©¬Í8°öþgý7Ž;vì°  ©¸!rêÔ©SaÏP’$ÉíÛ·o¶ÆX,møkbÓv¬¬Ñ5Ã_ÿþû￟›››K“è 2è3F£‘&jÜÜÜÜzT,ùÍ Õ•Yß,鸼i8ó¥—^z)¬kÞ¾H£ÍYÑýtɳn0en/ÝV¸LPžtI¡¬D¡¯¿þúëðEynnnîûï¿ÿ>{Yì§|eQ¾>Ú%UIÙäõ.8qâÄ?þøã°õz½ž.ÿ”ÊŠ g-»5 ù9Æ5)hžô:L{ßÚ}v~~~þÑ£ì _¿Û‰{(óæ3ǹ'ÂÞÿ$I’~øá‡‡>l÷ýy5 ¿e/~×]\\\l6›Ía–©é޼¥[ÃåZ³®™a s'LMývÀ¥¥¥¥qJXÝN½^¯ùå—_¦9NžÎeSdh}8’'I’äÙgŸ}ö…^x¡×2”¥›¥¡óÄÓò†ÕûŸ$‡R»¤3333—.]º4===½³³³säÈ‘#YçT§ù7enoyyy9î{øaå+ûxŒºÿ÷ÿþßÿK‡þ%ÉãÞ¥x¿½úꫯ†I÷†!+ñW’ä'bJG;ähH{£K/Îaf_Oõrm—µ°!ž×û¾X妺ÉìßÍŠÃVÖ~¦wY GA8m²ÝçVVVVÆadC˜“¢¬é(YÁ³*G­iüŸ2:kâs6L4ÛN€Ä4€A §ítî‹NëM–%^1|I.{ˆs<\/äíêÛ«¼µ?óæFv³ömÞZÄel/Ÿkí¶™U÷N¬8ÚÝmÔ®¬òU}<ªœÐí:íír$¹—|øá‡ vT±~|§µÕCq=Óý™w¬{ PfËÜV¿×UUåÊRdJJ·õh4ð¼(k;e Ïâk£}Ö—uÌâýÖé™8Šûù°ˆï[YÇ4ëZÅýšõÜ·Ü,aÚíãðZlw™²Zöˆµð91îÓÄúQÆZóƒj—…çI¯÷õ¸¾ÝœWÝ>¡Œ£dè#–———Ã!Y;;;;çÎ;7¨ïOçÕïÁD};-S¯/ÊÆÜiþM¿Û‹ç&ÉãÈXÑè|³ÙlVE«º|eQ·³³³óÚk¯½ÖiØÓ‡~øá0–”‹`%IûÞ¢0ÃíÁÁÁÁÙ³g϶þîèÑ£GãÞÇt4À(¾0÷«Ê¥sª÷R¼¾¾¾žem§L­V«õÁ|$ÅçE÷º~t¯=”ÝþÝ(îçÃ"~¯.T«Õj÷îÝ»×)ÿÍ(—'Ke%RUñ»GÑÞÓ Ò•‡Â Q*]—~aaaae;lâK*œ£7ž³†’‡¶8À—¾+vsIÚIøî”æÈ];nS3›¬‘=ý®rìØ±c½&i Ø&I9ç#¿5”Àìììì_|ñE˜òÂ… †1Üwsss3+iPžn_’¤÷ú¦Qž0‚&‰{óÍ7ßüöÛo¿ýýïÿûcÇŽëu S¼Þu’ô?Ì­L£^¾q3Ê=ÄñH^¦ç´¦–•œj\×x®ÕjµÏ?ÿüó8X2² m:ˆ¯é^3n—µ*-,,,d=Ûz9vwîܹsÿþýûéÏyS òdeò/$‡ý|d ¡ßšÍf3ý½©ÝË–Jƒ-ã4mñS¯×ëwïÞ½®<óꫯ¾Zöó|”ßÿŠH§uöÛ&C |öÙgŸ…/Úý 1½Ô·V«Õ¶···Ó rsss3~¤“ÿøÿø°²næà b{qêÁÁÁÁƒ”SºþU]¾²Ç¨å¥ÅâÕ3ŠLUˆ£Ô–«Ùßßßÿý÷ß{MÇ­÷2¾7%Éàs·”m{{{;~ O{ѺZWÖvÆE^Æõ¸2Mà' ‹3L&l½YÃþCã?µµµµõÔSO=Õiýð$ùµ·9«aYÖvÆIÚ`ìuÍ襥¥¥n‘“¸Ÿ¡[[[[Ÿ|òÉ'>wppp°ººº:ˆ2Ã1°@œ]û°'ýë§¾áPäNIâ²zGy˜56[[[[.\¸þ.í…ÌjpÄ™qÇ!q^V“ÃÔøO…½Ù¯¾úê«zËÖÖÖÖ²zwÊÚNº†›êf^ãÆÆÆF8Ê-I' ·—&Œ‹Ë˜ŸŸŸï&0Jûù0ÉËÑŽ¤€ã¡Ùl6ó&ÉãwÎÓ§OŸFN.+\"6I'j¼wïÞ½²:/Êñ9jÓƒ«Ô)×@Y¹Èê9VÒ¿Aè§¾µZ­öÒK/½”þÜËóQf]Ĩgúõò1ëëëëY µµµµ0»q¼ÒÀ(¯ŠºxñâÅx)ÌÃF½|ŒŽ¼!ÉéòGËËËËaBЃƒƒƒ÷ßÿýQoDg­„ñÁ|0ºíQH£îq¯õôôôô믿þú ·3 âuÅoݺu«ÛÆÃÖÖÖÖ­[·n¥?—µÓ~†²Å3£8&×ÖÖÖÖóÏ?ÿ|0ív5—lw³ìU:½ ŒªÏÍÍÍ}ñÅ_ŒCBœ¬sìÊ•+W:ퟴ¡sêÔ©SU•-ͯÐéúªÕjµ3gΜ ®¾°±±±Qô­×ëõóçÏŸ—ö0—µÚ³ŸªÓl6›YS»ßkã†}ž°íÓi54zSz k¹¨~“þݼyóf˜H"o©Š¥¥¥¥Ÿ~úé§^Ot«Šú†ëŠ·ËšùÎ;3~ï'Ÿ|òÉ8öþ§²æi.---=|øða^6Òåååå~Öž>,ååëƒ_-,,,„A€gŸ}öÙ^xá…a–©¨øk·Ìá ¯ËT»uÞëõzýîÝ»wÃ{tV²Ôt®é¯¿þúë0دäPÖvhÏ~¨Îúúúz‚¼.3 WIDAT¿A€íííípd»ªT¼Úýû÷ïß¹sçN?å GúÂÖ«°·,kèA·Âe³RYCë²<|øðáÛo¿ývX†°|q]ûzWU}Ãræ½Ô¦=–íz¢ãòéÙl÷7eo/Öë¹Ø©7¨¬Þ~ËWÕþ+ëú(ã|N¯©¬¡P½Ž„(³\YªЇÿ†×n™u¬be ].¢ªó«è9Ÿ%>{ÝVÜø,k;ã"®o¯×w|nuz.ê~6ÈSÆ;Q|ÿÈj?Œšð¾XäÜî=©¦ p @Î;w.^^+vppppúôéÓãÜžj6›Í´Çùã?þ8¼`F#íÕXYYYå5F»Õl6›YëÓŽŠQ-ߤ]‡Å8 EÎZ³½“*ö2O±ÛuêÛÙÙÙÙyê©§žê÷z*k;´Wõ~žç·f­˜‘5Vîà° G5Y†5¼GV5ü¿¬ÕŒŠlgTWN›@»åµ’äñ°û^—îUÍf³yäÈ‘#÷ïß¿ÿõ×_FrÖÖÖÖ6777§¦¦¦Sã?•®O{äÈ‘#µ++++SSSSe¼Ðsù&ñúGá’£úPh'=÷ÛÒFöÔÔÔÔÑ£GV¹ÚA³Ùl¦K7f­¸ÚÛÛÛ{æ™gžÉ»¦¦¦¦Š wLï½/¾øâ‹q’ײ¶C{£¸Ÿ³–™ÌJÌçõÙÛÛÛ»víÚµªÊ0LwîܹsÿþýûéÏí¦ÄùzÊHnþ /¼ðì³Ï>þ®—íÙNYߌ‰pÈM¿Ã•?éµ¼cŸ5$k†îáÚngXSFQ\‡¬¡®Y÷çL†I$¿-w»iqýŠìŸNS¢‹Ü›»­CÞvÊú.`Œ¤¾ ~òt;?Þ92^ÒÜŽÛ¯ÊÌñ1®/µ¡"ù…•k=“ˆëžø ƒÊÝÔ­Ý3:T÷`.²²¾ #á n\nÊ”£›€Fäø {n=Ћ³ýœ×enkâ—Û¼òÇA½ÿ09&9$¿-{Vð3~t³oòžÑñ>ï5èZd;e}0†Òè߸½ÀRŽvçÄxsmÿ*~Që÷E4ëÅxœös<´?/Höy9„É’—!>ï¾WdÕœq tÞ{ ´g=£ãgU¯A×"Û)ë»el’Œºõõõõ©’02®âaé*4ágVWWW{M²·¿¿¿¿ºººþnzzz:L~ÛëKá œh6›Í©À ¾&0þiØ0ªgL˜0~W«Õj»»»»»»»»µZ­6ìÂåú€F£Ñx€ju 4›ÍæÔß=zôh«Õj ¢`@y†> 4333óôÓO?=ìrÀa3ôÀsÏ=÷ܰˇÝÐ@õþ©ÕjµŽ=ztXøÃþð‡ôßÓÓÓÓÇŽ;–$Iò·¿ýíoáç⟀âz°¾¾¾.9 ŒS`ŒlàÁƒ’$I¹pÝ"q&Ìu‹ ‚ 9¼ÃÔå‘ç2éG @A"~ðæO"lG4wƒ"Xá^6ïy}3¹NpÝ"q%ìu‹ ‚ 9gÏž=k2¾ñW¿úÕ¯L] ‰'©T*õñÇu?ÂÄô:B$xpÝ"‚ ‚ ‚ :ÿ/ê ‚ #•J¥þó?ÿó?ÿïÿþïÿ^¼xñ"êþ ‚ ‚ ‚ ˆ5@KX…•…WÕY?èXxÌŒ ‚ HW"ʪ(J.$ª¿h"C)‚ pd }L&ê‘e(Æ„@þ‘=GÙ^eŸdÑ_H–l6Á™n}ÐÒ]A'( ²¢w=ÁÏÚ‡Ì/Û4q[æ©-Ø2Øð€¼;¼ÞŸJMï qÜ?‚ÇÃD˜78¢AE!AÂGöb3™Ý9,EC·b›jñ«¿~ûS­V«²1örP^‚‹0û9Dy=8«÷•8އ-óÔlìG¸¨Ö®—ód?  £qÜ?‚ÇÃt,úºÞq nu%Eý=:êÎ’>ÝŽhý†¥ˆóÚ·›~…mÓý…Z¨Â쫎wD{rýâ äÁ/hâ4¶ÌS[°e<°Ñ Z»ºŠ`/׋Óþ8"² Š»èaÅq0¡›Ÿm¡ ªÇgxG6LÍ]™Ò ݨüc‹ÀÛý5¡ŒáÕ [<‚z‡„Ñ S:n³¶Äxe­²ežÚ‚-ãýˆH(ôUc'’¡p?}1ÿoZ3¨rI2!pètmÙø Zdí"èùŒ €øc‹À”µÝDM)#dx]ºãÔ «&Ô ãqÃÂä3Èñ°ežÚ‚-ãýˆÕÚ…¾»Tç:ÙšÅýô}p<"–ø¯O/’‰dRQÑØÚÒ×n&Ìù,{¡™ÚèÂh£›±Aàe_ ²¿ÐØT¯x=Ìz'[’"zí‡I+M'XgL2ƒ[æ©-Ø2Øèñ#¼;Žÿ5‹ûéûàxD„‰ø?ZAS.ž:–^›ìÆÆ´ŸÝ±5ÎD1ŸeJ¡0¶xÇÄ—°êåIæ¢ßþB]SÙù ]~úçÅ3!¥lØýP½{ cÚIÖM“.É,¶ÌS[°e<°Ñ£z¿ÈÖ®©5‹ûéûàx„Œh°D“ßtü¿i ×  wˆZs¤£±º¯ÝHóÙqä/rSJ«0 ºªCQ˜ÖÕa@v…lýÜ‹W%ŸéƒGØý€Œ«j/è$댉æ)wd/×öÚç¸bËx`?‚¼¿ ýóšKËÔšÅýô}pDíê|7 º1a‹í˜œÏ¢õ¨þd}0%8Š ¨Q5ƒWyö&ž³ªaí'~Ü3 o/‡ÙZÛÛÛۡɵU?ü­AXg¼æ&ýN÷äxøYÓ¶ÌS[°e<°rTk‚÷Ý dýè¶§:ƒë¾#mÚOe}ñêqçñˆ=:.ã&‘ 4ï`æ÷ðª¯ïÇ Jk§—géeÂËÚ1-ˆ6Ó8-Ô rEx¹¦Ž€®zùóÆ^tý0ÝÂ;/ vò;ïT{LXëÒï! ª(õ2^2åyF2åƒ)r”ýð"¸š¶Îè¸/Ó×…þNç]ÔxøyFaÍ uÊÐy÷ີ³Žã½Ú 部Ÿ¯^½zõá‡~(k[gšPLóˆz?ÕYÃô=êÈ$:ïڨǣc°MÀ{°¦ ïÐ'º64¶ßT€™™™¿g]÷‚—:,a/îB¥-óYô}Ñœ•½pE߸?+ÛÑUè$oÕÁk\¤ITYtöIÓgrÈ‘aI9ƒî‡áÕ¤uF׎ܯn̳Ž@5guÇÃï¾Öüˆ‹×­ýð²–!˜RÑ×òó^’å~êeýîííí}øá‡êÊ–P%@Ôï—ŽÁ&€Žå]ÕoÑ‹™ÝLtìwýZvÙûõ{0—-Ôÿ÷ÿwÑß¼X¹Â°Ì‡éi:óÙ+ÐùÌû.·VT/ëX›d›"d>«ÚÓÕ–²÷"[/qxVȺ—ÍqÈ3õª$ƒ(Aͯýò«XôûÞ*!ìõ ]³6„fÄ[l?x‡8]÷¼£#þdb7-ÑÃ×U6ȸ®EUˆ»–Éx-Ùý˜0Âj#(‚˜Ï"Á:ŸuÆS%(±ë*îÊÇñ¿Þuž§—Ž×8H?múéKPÏÝëË<Œ)Yt¿Tï]¿û›-ý€ZitÝã½Üw°¹W·åíííí³gÏžõz&3á%az~ÄA`ËzÁ~¼#(·¿*¯ý2åö~ê8á¯ß r«˜]ŽuáNb``` ¿¿¿Ÿüûþýû÷ëõzü»P(VVVVL´Õl6›kkkkäßét:=444ÄûÞÐÐÐP«Õj©®Y,‹•J¥Â~N&ÞÆÆÆF"‘HÐk4“'OžÏoÂ)Z³Æj€dÖ&žfÖJŠúË^S¥¡Oã´µ_u/4´¶Ö¤»V.ßq® ›ÏV{Éû»®D×bçœnø ý̼¸Á‹æ|Ðñà¦ðâ]D³W-¹ÎZñâÄZÑéG¡:±ÑóÙÛÛÛ3¨Z7ìu‚ŠŸµ¥ëC€¾/e÷­:'ðà­ÑÜg¿”Sd®F=?ü„7‘€4êñÀ~¼ê]*{§AÞM~߉ºû™iO¸°öSy*IÕ/^»¢õ¬ã1öxèK‘•åÉ“'OÚív›þ,•J¥x›V«ÕÚÚÚÚ‚´G[_¿~ýúùóçÏÉ¿‡†††XËÑ&±×j·ÛíÁÁÁÁ¾¾¾>‘¶Žµ`©4øõz½žL&“ÇfgggÉw²ö³ðƇ@kkeV»t:žœœœ„¶)Óüª¬ÇPD×áYølC6ŸuX\\\TiµÇqž?þüõëׯɿyóùíÛ·o÷÷÷÷Uý=qâĉS§N’µçW“n©T*5===íå·ÃÃÃÃ_~ùå—2 ³ŒK—.] Ê  V«ÕLZýç¢çÅ‹/ žÙl6{ûöíÛ¦Úw=‹¼ã¼; äóù<ïo·nݺ¥ò&‚îgªuÃzÂìîîîŠÖvÿÀÀÀ¤][ûAPYidèXgD÷-²ÀµÛíö“'Ožˆ®G¿; ¢wh2™Lž9sæ ¤ŸQcÛüˆ[ÆûñN€¿xñâEÑß]×u'&&&xï´T*•zøðáCջɋ‡$Î~ÖjµZKKKK^ÛòÛ>‹k÷þþþ~___OÖ‚öK$ >~üø1oîèxT…=ºÄR rÝá¹ó²îΨ Æ*vwwwi%¯/—êf³Ùe„ï¿ÿþ{òÿ:.Í4´å“wÈ$n‡ƒƒƒƒ¢~øâþ/ú7®0BhñŠ4»<-±H©ä8|%”M¨æ³ÓÓÓÓ´2éððððÍ›7oØï± Þ|–)mèg&S(ÑЂ‘Ha(ê¯Mˆö/ÇyßÍçæ–H$û·û·¼{§Ý‡EÊÈ Ð2å¨r¹\îÑ£GtÝèƒ € ͨÕj5“ÊXÙºá€daT~l¶ôƒÐn·Û^]`¡{ç™3gÎ$“É$û¹l’…'‰Þo¼ÏuÜV£Æ¶ù5¶ŒöC}öØÜÜÜû————¡¡3~B$Úív{nnnb˜¹uëÖ-“ xÒ~û)á³Ï>û r2…ªH4Jöxè;€HøÅ^ëx ð`5Žô –××uÝÇ?†Ü‹ ú寫Á—Yû[­V«¯¯¯õ™Æ”·Yommm‰¼"t…Ñá‰'|ÊúÉû¾L m{L¹l>ËH$‰ Z9B+•dÚo¶?)ž…•·Þ ZÚ8xkˆö/6¯G¥R©@bÝçâÀEùDL FãôéÓ§ýæ›`Éd2™ÕÕÕU/1ô¢Üa ÊëÐjµZ7oÞ¼é8ðõ©Bæ™À[ª< ^=©lé+®uF´Že{Ìz© =î³³³³¬· K¹\.ë\ŸÎa rÞ°q~D‰-ãý æ²XúR©Tyyñ¸xñâE?aõz½¾¹¹¹)ûNÖå0öÓ(Ñ5ÈÚ<±Sˆ,b"Wfo8µö›v»•uÿ'È6k]aDtxâ Ÿ"ëŒãð]²DßCÀ (‹Åd2™ô+؉„zÃe_Ø®ëºÿú¯ÿú¯¬ðJÿF×-Û&x}½*•J¥V«Õd׫Õj5ÞA\´ö ¤R©TÚÙÙÙ‘YGFãòåË—Mk´!®•F£!KŽ$ yùÍœ,rýW)Ò¼J¥R—.]º$ú»È H¦|ðrHµ¥,^¬4ºÖ™¸íA: ÅL¹Û:?¢Â–ñÀ~¨ÝÿEž<Ф4~CvT}uœ`ÃcÂØOã„ÍãqÜoù&è$ãiÒy‹ßDü¿íȬýtfË ­ýYÁ’*yV$ßkÂ*ë]÷‚©0YL9;‡T,+„yqç´Ù|–A+“XWó|>ŸWe“¥ÛiËU^¼Ð‹ýýýýf³Ùä=kòœE÷„p² [v‘F£¨K¡P(]L†*Ab¹\.“0 •wDX¡‰D"ñÏÿüÏÿÌ;X4Æ;wîП™pa–)8e^K²½ØË{Ζ~ðбҘ´Îج„„ZMVmžQ`Ëx`?Ô°g ™gZ­V«é†Ø@Ññ8Èf³ÙÑÑÑÑ úö~J²íG}&ÕûEE¬"  âÿçç‚$kã) ‰¥R©Tꫯ¾úJtà¤~¬`És×¥4Ó ¶¼ ÓBò´¥“a"[YB?ûÌEJ(›sÕ|†P©T*ì L6çÙ1öª(áiáɘóžµJxê´0 BÐq¯Õjµº²²²dd!ž0-óŽp½‘hoQÍ©þ?Á~;777§»@”X2§, ¬2‡ãè'fµ¥"‚xmŠE‡–„¦EÖ3*%±íó#llì‡ÚK”E”ôäwQ½'¼† ±‰™UÌÏÏÏ"–і󈊰ª¬SÈ\¦E/$^‚²£££#Ѥà¹ÏÉj‚Ò‡,Ö ÍÓ.’þˆâD«Õjõààà`pppP4´õœ'XÅ)᥵ŸE«%sóWý]ç¥!Údi¡RVŽ}æq·(Ëæ3”;wîÜ¡7³D"‘YcYAM¤(¹¼—8ÞK˜Ì^É/Ê8 ;ÈŸçmHª ËZÏyq¥R©Ð%üDŠdU<Ë€—øq™e’ÁT€È¥œÞðyVåßýîw¿£GÇdÉ<âbQ–Íg(¼ÍL$`²‚šHÁ#ÓÔŸÿôg´çOyÐßßߟËår:UClC׳A”YÞqÔnî< ­¬Ñ‰ •’Q´U¡ét:}÷îÝ»¤ ݵî%s;Ï[AÕ¾ªUìªtjVÛÒÄNp~¼-ãýÐCæ‚?555”‘N'c|||<މ2½$XDÞØ„r¬»³N .™L&3>>>Nþ­˜IÒ Q ¢„¹ß‘ø^’ âͰ···Ý$d–9™û?ÁT€È¢L'ñ`ûÚjµZ_ýõ×ìïˆð)òˆ‹@é8êù …uiây°›´l>Ëò68ÎÏ_Úí§—Ë冇‡‡ÙÏY/ü£Ñh4þò/ÿò/¡YãF Ïçó…B¡ÆZoµZ­k×®]ãiý!ÞI*d{±   @méb'8?ÞÇ–ñÀ~À‘%ý£óÑÁíÛ·o{}§•ÞqB7Üù9¨Ðn·ÛOžC¯#óH¥R©………z“–Íg~øá‡¬5”Î%ÀÞá—¿üå/ý QQ" 1Z]]]¥×€N»är¹Ü£Gñªˆ<~üø1û{:ü£\.—‰’î½ùÁp ¸sçΞRUTrÖqä™ÛyÈ2¨«¬^EDÐ! Ð0[ú¦ÃÃHY_(QTÒ¡Ûç‹-ãý€qâĉ²¤ìúSÅøëx“²FC×uÝÏ?ÿüs™lâ7<0lT!!$:Èó„QC÷R›Â­;Ž¨Ê¦Érˆú%³ª‹êG‰¬é¬ ¾Êê.kCD¡P(ßçr¹Üááá¡ÎïUã$»¦ŽjÐ÷!C4F¥R©Ä+BB&DÏ‚xK莋mèÎg(©T*uöìÙ³Ž£?Ÿy¿¡Ç—í/ÞÂ{6¼ßÅíyyYç„/¾øâ ¯û©lŒèçì8êõjz¼UíAæ²×q•][V†ˆEž%Úk^ÞY¦„ˆÙÒª1÷ºGÊö5Ñ3”›‰½BPãAcÓüPݯlͪ’+BÇÊ–ñÀ~ü„—³òÑ‘øÌ#{÷èîeª÷™w~ß'^jÿ=Ñ5EãæY0ŒýTô³‰¬êŽã8cccc" ROOOkõ)•J%Ö_f-å¹ìÒ•Ž;vŒWjdzzzšLvž ­9ãy4ˆ\ˆ ~Ýÿ ²0Ç»lÉ,/¼\Äó€ÕÀ’°‘æ6nÉ¡tç3”v»Ý~õêÕ+ÝùLi¾³Ùl–Õ®³^"¼økÞïÈwãò¼uãÊçÝZþâ‹/¾•„´+#òœ½\7 ÉBU¡øàv¡]jY×kò‚¼â’F4ŸuGòð2Ÿ Ð$j<÷g//é8P¯×ë÷ïß¿¯óZÀd+6@¨Õjµ cý¢*í9ÌxIj¸¹¹¹©rÿƒì2×ÇQ漏£v+—)Xe± Uh›JkK?¢Äôû!n.í2p~¼-ãýø Õû†‡(éŸ,I¶ãè•IVÅÁÓFHú Ë†Ž‘‘@çë žÄƒÚ:ÑaHdi MXýfgggÙXR ê–N î lâ®F£Ñøä“O>ýŽ7¢ƒéÒÒÒ»é?þ<ϺÀ¸›Íf“µ Š4f~³ÿ³Èª@3·ŠÊÞÞÞÞ>úè#ú3:¶—·áúé§ŸÆ9£<Þ|&‰#ÚÏ^àiÉU^#4q)ÙHà='¬å¡Ýn·¯]»v jíây™F•0Òq`‡UUˆÕÞqäûAeÝ•åvQy"¨²<,ªÃ¯LkK?¢R‘†EV{¼S¬V8?ÞÇ–ñÀ~¼Ô³‹ Kú§Jhõ&U%ýåU—TÁ ª, ­x­„Õ ÄB Jø$r•®t-Ð"ÆÆÆÆx‹heeeSB44¼¤]F£qùòåËºŠ ‘@#ÚôdÖo¨õ”û?ÁD›ñžÐßßßÏnž´‚ˆ·áŸ={ö,OÁ§€<üÌg:æÙÄ|†ZñEZr¨`G Ñs¢áy9ŽØ›‡Åëþªýš¸  S−UÞÈd2™ùùùyÙïU_••IG®:üÊ,(¶ô#j iXdJ"ƒú_|ñ…­ ¾ºi~@Â6lìÇÏždÞŽ„}2Å.RöO§ )ôZÊDžQª° HÈ ¹Î—_~ù¥ß~ÚB,"‹>O¨”y ˜´ÒމˆG€ÈýB¤µ«Õj5H(Ïú%z¹ˆ”'¬EB¥²V9Žzcóª|‘mV¾ÈR˜H$츰Â#»áŸ:uêOÁ¡ã²e+^çóÁÁÁÈR Ï4PW;Ñ‹?Ž‚½cccc'Ož<Ésé/—ËeY†Z^Å+YÚjµZ}}}}AeôWí‰Ø„JK;)cÊSb©J¨zRŽ‘ô‹Í¹Q( þÈò38ŽÜzì8úû±l]É,(¶ô#jdixyÕûÔqÔu2¿yå}m¡›æ)/*ûŽ-ãýø9ªzÇ‘{‰¥R©”¨Rbæ8ê²²>UH¡hO² ‘GUÿÀÀÀû¹J‘”Íf³ËËË˲6s¹\î‡~øÁk¹EÄ#¢ìª¼ÍT”14¨L^³ƒBîE;&¢Ì¡¢ïñƉûÍûo,UÙ-ƒ2nªì¼GGüñƒ>[¿1ó6Õ|¦Ñ‰abd 6•1ù‰025ó2*CÖ¶) ï0ÆÒ“Y¦!÷%ê“-ý€d–fÙ\¥¯«SIBÔ'zÜýìwAg­¶m~ø©àå Ä^Ï–ñÀ~ð>cö|9WAï<“p IDATGu¦*+ø9ÃAÇËëþ!ë?{ÿ:gZÞógïç¼F€ŽÊ kÑ2þŸG½^¯'“É$Ϫ$1ÔM¸õøñãǬÿÑ£Gè‰ÏËÄN\Gynªù|>OOîL&“ÙÚÚÚbÇ“£U‚H»«0Ï_>À·xrQÍgdÞ<‚¸‡l ?á%Á¡WT‰!¿¸®ëÎÍÍÍÉÞe*wG¸Y‚j]ñbÒmé‡-°ïlš™™™r8“yU©~KPy Ø@§Íh[¶ŒöCùDÇúÅf&­×ëõ©©©)èïEÖ§0ê´Š€ÄõA^Ì"¡SµÉĹ”°æ3A¥PQ ð䑨©×ëõ«W¯^ C N§ÓwïÞ½Ë~^( AÇZCKiª²L{=8©Ö%«|·¥¶àGY庮ûÿðÿ–¢+ :m~x)+JcËx`?~>?2™Lfuuu5(á’x7•J¥d}P•—¥ìGãããã¶V΀ädàAJGB«ÿt2Ö+t,ú"¡W¡8Ðn·Û—/_¾ Yår¹Ì;E]_W•Áâ.z)¨¼ÐÜ<ª1W•É‘¹Ú’ëÇ!Ë=§^¯×/\¸pZêPD¹\.«Â_Ød^²¬ýÄ;FåEéTÑ&Ë6îGa©JºÈîöôÃ&t•î„û÷ïßÿÍo~ó/¿µ•Nœ•J¥âu­Û2Ø÷û! …5N¢dUiØF£ÑX___×iŸçILcsY@ÝòÆâE§k@olY‚H«%Ã%¹%Xè¤$m"DI1tî—¤H•P$Œ¤W* IOd‰1xÉŪ„LA%—ìfTI[TÉiTsÒD¢ä}¢JÈÃK2.v«é°ßçÝ?Û_Ýän:÷ }º×£QõŸÞ‡mé‡.a$i‚´Cã%¡˜ì÷&ûD¯££è懟$€,¥äz¶Œöãý~ȾKž—Äɺ{”ª ?{^ ÃØOuÞ«ª„©*üÌ;Ç ~<‚œ'‚ Lg‘&œ={ö¬,kµêÚa šˆÝÈæjÈ{xÐCÿ|øá‡¾zõ껾D†•Ò÷õà€(kdÈÎ q1"«æ_\îAAbG¡P(àAA¤3 …C¿V` ¯žÎ‚ ‚ ‚ E©T*¡g b+úŠ ‚ ‚ ‚ ]€,$ ­ÿ‚ ‚ ‚ Ò!Èâÿý$‚EAAAÄ"DñÿX© AAAA:Yü?ZÿAAA¤CÅÿcÙ?A ªLªa€™†AA 8uAAA T ‚ ‚ ‚ H€ AAAéP€ ‚ ‚ ‚ ‚ ‚ ‚ ‚ b¢RXAAAA:Hé2,-† ‚ ‚ ‚ 1F§n9*AAA$†¤R©Ôööö6Dø'ìíííe2™LÔ}GAAAH©T*±þöööv*•J‘ïT«Õ*ûR©Tвß‚ ¶R( <å) ‚ Hdð¬ÿÕjµÊû.«(`•‚ È;xJST ‚ Ò)ºˆ7Nœ8qâÔ©S§È¿]×uyß]ZZZjµZ-òïþþþþ0ú‰ 2™LæâÅ‹£î‚ "Gä­Å‚•°äç  C8<<<|óæÍ›¨û W††††Òét:ê~ ‚‰¨l´Ê£ÔˆWëÊÊÊ äû++++èýЉ,!»­ ØQÐ!$“Éä™3gÎDÝA¸rþüùóQ÷Aø!Øð!‘å:¬$Í©T*µ°°°H$¼¿‹ÅâØØØXÐý›l6›}ùòåKL„˜æîÝ»wEƃD"‘XXXX°Mù„ €˜òöíÛ·ûûûûä߉D"1===Íûîäää$=1Ñ[á%D—¹è=›­0F*•J]ºtéRÔý@DN dÝl ^^^^Îf³YÞߊÅb±R©TÂîSX¤ÓéôÝ»wïFݤs( …|>Ÿ—}'›Ífoß¾};¬>!Ž×*(Tˆ}Ùë®cTØlâó‘K,–NEû°MÙ»?.ïrªÄ¬Q{Z î>—5†HP P^Ð<½¼u7>Ý6È=ðªˆÚ«ì–¨O^Æ š|Fçša´@_è…NÚ8uç¢W¥”ê9xS{ŸÖ7¨†jí{U°ªÆ« ÈO @Ùûú.P½»‚8cú1ÊðƺŒ<„×ó¶tç/ψsxxxøÙgŸ}Æ›×&׸j-ªÎ à‘>øàƒ¨ûÐl6›CCCCt†®ëºÍf³tßD¤ÓéôÐÐÐýÙõëׯÝn.—ËýðÃ?ˆÜÞÂ"N§wvvv  Ô árfffFv ’4‡9ЛôÆÆÆ†(¾ù¹ f£Ñhœ …T6›Í—¥ ‡1Ñžþý÷ßOÿ{kkkK¶çööööz,Tï”ï¾ûî;Ýk"‚8Î;A{uuu•÷>ˆÊí?•J¥>|øP–xÕu]÷êÕ«WëõzÝT»‹˜@äú?555õàÁƒ<ù̦¡@d@+D|KEÙ²éMOOO“rc™Ëår=zd£@:333stï° bU€fäívJ¥R‰}i,...¶Ûí6+lÒè*eqž„t:~øðáÃ(­ñ‰D"±±±±¥W€hr]×}üøñcú36ÿ Ë©S§N8qâ„Nû©T*ÕÛÛÛ+ú{«Õjmmmmé\A„ JReÌ?ä5555åUø/ Þ¹dmmm-JCr¹\îÞ½{÷ØÏéõÔl6›®ëºôwòù|ÞÖŸ)ˆà/²ņÎ;Ì}È´ÒâÌ™3g’ÉdÒä5½¢Óžõ= úûûûçç M±‚Ú@>ŸÏÛ C¡P( µN&“ɌӟÑÂÌÊ+S°ð” "²Ùlvyyyzí ˆ2C³¨üßëׯ_?þü9ýY»ÝnïîîåeŸèïïïý}ÿíÛ·ou®‰ â8â÷A”Â?äU.—Ëþ‰ –<á¿\.—ggggýôénDro¾ÖëõúÔÔÔ{¨¼/…@REí  ?K0åjDü¿×þéöů¥™U¸ˆú¬zFÕjµê7~‚ŸØ¡ r¨01G½®<…_qþº}ˆ¼õJ¯I‘BSg^x}.Ðñ jÿ#Dá  ëñ¦Zÿº{—éë!H·£2Ù˜‡$Dï¯ïS9 ï(³*äÔ ç‡nŶªsôüÊcÆrèXŒ3333aLªÕjõàààÀK,7q5õ;è6Äÿ;Ž·²U^ãSMð»ßýîwÄæâÅ‹ÿú¯ÿú¯‰Õ«Ñh4ž>}úÔd{©T*%*Hp]×9ÆP,‹&û%‘H$VWWW­Ò rˆ‹g…Mð¬ÿŽó¾eŸ—×£Õjµ.\¸pâ)‹óTqïÞ½{6Äâg³ÙìèèèhXíÉöQ‘GÆîîî.ëÖG£ûŽ8þüyÙß1þAÂåã?þ8î Ñû JË?äÕh4Ÿ|òÉ'&Û]YYYA%â—\.—»qãÆ ú³Z­V“ý®R©TÊår™þ,êP€ãŽÃ¿!ÇywSD :yòäÉF£Ñ`¿3>>>†°B´¹ºJ ù|>ïUûkSüÿ‰'Nœ:uê”Îoh÷û°yúôéÓÍÍÍMÇyç&ûÛßþö·ä%°¸¸¸ø¿ÿû¿ÿk²=•[m­V«%“É$O°ªT*•žžžž(âÄü& i·ÛíÁÁÁAV©AÃnD,Åb±HŸ=,¨Æù9"7sV¸k6›Ížžž2öÐyI¨$Ã&å'$hDsYwÿüùóç¯_¿~-º¦®¢U¦0Àø bUûú믿Ž{Ýî_üâ¿8wîÜ9öý•ðyGµZ­Öµk×®µÛí¶éöWVVVlµ¬V«ÕnñL‰+?þøã§OŸ>M¯'•ðO˜e×âŸýÙŸýYÐ}–­/r7Ú (Wc/ž2*aÕY•¹Å¼~ýúµèo~ž—Ÿ€B¡PàÙÙyèw^ÉÆÇïKÀ‹Ë®®k~šA¿e!áBì‹L¶vâœЍ\Œ)«»ê™îííí}øá‡ªæ êâgî@]ùÂ<¤AËÿ±ÈÖ€ÎsEWe1—už£l£ÂO@Ø®ú¢öl|®ô»ÑÆþŨ'/ß!¼ìî뺋‹‹‹ì—Ûív{nnnŽu}¼téÒ¥ ( ÏÒ)*«å8ÞÜ/®\¹r…××u]Yb¨ ¹þÇüLj\T£ a¨×ëuâ@*#«l|¢ÈK,ó}}}}ÒaZAu€xÁ”Ëåòààà`Zü8´çP¡P(ÈJ;6ƹsçÎýáøÃààà l_Ìf³Ù ,_•J¥"ò&£ 3a©h?üæ›o¾‘ýNö÷D"‘eõ§Q%rÝÝÝÝõ³ŽDŠ T, ÈOT«Õ*›ÐÖ¦’]qGõŽrxÒ?(³³³³¼óV:NONNNšjÇ/lFùt:~ñâÅ Bò:hȪéù4*o_U²Ëã<7r^æcÏõÑKÙ#ªX!¢ 4ÆÆÆÆdN]!KtX”YÈâV[­Vë믿þúððð÷÷‹/^ŒR»Hœye¶L!zVA¶ ¡Ùl6Ï;wN%é†k„uÐW ,F£qçÎ;¦ÚóƒÊS!,Ïј™(Í)*GCh4Ë—/_¦÷ȱ±±1™`fff&(M¸H‘"Å Äí^•@×Oèííí•LTŠäçð,#¢µÎóJ1陃Ø*¼4ê8ÝN@õŽrœwçù ²ó7›ÍæÚÚÚû¹-¹¼DrN"‘H,,,,èžÝ –în›Ï·oß¾­ÊççÜ×1?nSI;QÝRÇy£ »h·ÛíË—/_æ \:B–¬V³_+.²ìýýýýF£ÑxöìÙ3Þß“ÉdòÌ™3g‚í¡˜õõõuò,6777½Öx•!{V&„.¿@„ D"‘¸råÊÙu胮è“Íf³¦„]‘ áÉ“'OÐòÿ>"!Ïoy7•’”'üTJ€ “ªbè.@ûA”—ò\Þ¼yóF¤du¸gœì>£ð,³‘Û#q›- ^¢à|>Ÿ§{r^©0S ƒƒâJ UüB¹èqP…ñxq_†„Çq€žŸŸŸßÙÙّŤ³ ó¢¾ÿ¨Û×b} "韊(bd9tr!ÐÏR–yeeeåè®èTíºsC%0›4Z‰rÜѸ®ëÎÍÍÍéœ[ƒó08®:ÄDE¡P(ˆ/LEDÈ"Ȅ42«QFˆê†ëÜsÐn+P%Ž.²‰¶ÔÔ†A2!"—Ëå~øá‡ 1ü$¿¤‘iΣö®°• „Y™’´V«ÕTacccc¢„^-qâúõë×yŸC”XÍf³)R²:Ì3NUÉ%lϲ¸£²4Ò‰.————U{g>ŸÏÛX‚qvvvV•ÈRM#—Ë冇‡‡eßa…3ZÏÃKøŽ*©l‰0³ÙlöoþæoþFÖf___{ÖŒúþ£n_Õ: 2éŸãˆ«ï„m´ã!×u݉‰‰ H˜j©T*ù­Œ¦:ªäÝóÊëÍ” êúÿþýû:†È0Æ‚ïoß¾}»¿¿¿O(³Šó6 ÓU&“ÉÌÏÏÏóþæ'FC$pA-5¢I…•FtpuœŸ«¬tTyl@æÙbÃÆï8ï!Ožá¡ò*{öìÙ³0söÔjµš¨úJÔ÷uû:@ʋߺuë–ε‰D€* ‘‡‡lïÙØLMMMAÒjµZUåU€Íf³/_¾|)Ú+¶¶¶¶dy«tå Ùü4iD‚¸þ놞„5æap¼Ýn·Yá5‘H$DõÓ§§§§Ù´˯È-Óol±hã„æ0MÚ°­4²„b´2B¶hƒÊÛ¢Þøi?~üX "d–_ù|>ïu“…V8Ž=ÞŽ,¡’ŽðO)¢²‚ʘ22™Lf}}}]u Y½tösÙú…$T…àaü?œD"‘€–Åýè£>‚*OÃLH©$ŒL–ìltttTu8 gKKKK2@'ÏÊ F””:\×uGFFFTûgÔ÷uû‚NúGBe®é¼ðÇy§ØZZZZòÒ® dc“jµZ5QN§Ó>|È{oª¼ÝtB©U ‘MÉS×ÿV«ÕºyóæMè5Ãó08î8ïj°³/‘|>Ÿ§ã¾ˆ«{ó¦]~S©TЧ|€ÄhˆÊ¾˜(©aSü¿ìÐH[`yÞ[5¦P¹Ù‚*‡'DÈÂc‚Fe±D~Žé1+ ÑÁÆ‹ðO˜-‹EÞßffffL*T.§º^U´+ÜÎÎÎίýë_«$"«—Î~®Z¿*KœÌò†ñÿöF> /ÔëõúÔÔÔ”ì;7nܸÁƜʼ, µZ­&DTN–uÕ^–¥Ñh4NŸ>}byúþ£n_t~‘ô‚®×’{‚èýí8ïÞµ*7nˆg…daU½1aP†ºþë̃(Æét:½³³³S­V«<«ÄUS¶IêX”I"ÐD²C#ë*&;@꺉ŠÙÍ«e1dJ}/" [ZzÐ/^•aô^ÃÃÆ8\(²žo”J¥R•}ñ›SCvmS‡5ÈËzè$ɉØ÷“*ωÌê¥cuWÄU‰¦dù40œÆ¤:бcÇŽ©âäé}R¤ür{2†ó¨T*Y2OžGçäää¤Ìƒ bS­¨[° Ie‹ÅbQ·lmÔ÷uû"’ÉdruuuU6¿¢Húç8â¼qjÙîëëëcßá####ªê;ÃÃÃü$uª0¨<¡Rz›0(Cò»Èœ,Qy¨@²Áz9”ë”ÙiØDÂ1¤üÍëׯ_Cû¢ºÑo½j ¡ ÙsbŸ‰,clœë@ë>w²y ò.1q}Õº£Ÿ)ä¾EÊ5H¦_‚×õ&Ëšíç™yUØPP¶ã¨ØPío²}L•Ár èþ*{¶&½Ådý‘]OµMÍͰʃ†…lÜØñVíÛì<‹ëX©î“~÷è|W†jþB®câ^ÇäèÈŸwhÔ÷Uû:ç&¼ruÎd¦*H˜òÐ=#蜽އèÝ#kòLUsÇļ€œ1tÛ‰rÌe¨ÆS5ïÓÿe„f)‹E™Eˆ>ì³î.­V«uéÒ¥K?þøãìÍð¬2F£±¾¾¾Î~î%Ú_üÅ_üô»4"íVØVUü kÝ–ÕªÖ­3Dm}Riëe–›v»Ý¾víÚµ0<lalllLdѲ E7#ór’¹>Dû=êÁ#‹ƒå™ñ’ìJÖ™w™*4Ħ\%qÍA"óÐàe6ë˜7›Íæ­[·n‰þNWôPåzuª’ÑAª ©òx-œËår/^¼x¡²BŸ;wîœ×ä‚QßÔíû!N§ïÞ½{×Ï5šÍf³§§§Gö~'x±øë$ ÕùV1;;;+ó½;ƒNzë7Ù'$ôD§Â¹f”c.ÃoxéqöƒÙÙÙY™ë\¹\.‹­‘cë"’ä*ÇŽ;ÖÓÓÓó‡?üá¯^½z¹‘[±ŸDh:ÈâÿÃÎN+:¸:ÿ@#‹QÕ‰ÛAì@劺¶¶¶&›Íf³¹¶¶¶f¾gˆ[ã‡m¤Ñh4._¾|YærªrE$Èòœˆº^@Yd‡pÙ^Žå4í"Is+•JEfÀÉf³ÙüÇüGYõÝD˪$¶*7t•Û°—$˜¥R©©Ëm‚¨ï?êöý`…Û3k9&Êj› e²ÐÝ„v¼ÜoÑ»S•ôV¥p’½óÇÿ|ƒÈ„ºaëQ¹ ]yåg Q¨â@ÙäKlgŠÅbñرcÇ’ÉdR5袛áM YýZ:ƒ!C¦e Ûb &xØU1ªœ .@çJ T‡‰‚r¹\†Äáªö4Þþ&ZG^…nU‘ÒN¶—cü¿7xÆÑþÊ{_Ú”Æ ªÒ€Ÿ~úé§²ø[U¢e–z½^ßÜÜÜý]&<©Þgºµç‰ÊD‰.(QßÔíûAVm, X—l‘wD° ™H×é%­¨”:ÁÂÉï|ƒ$ÇöRI.ê1¡Êé›P B”|Éq~úu]ox"Q&d^9BÇywˆdÝzëõzýÂ… ¼º>Ë´,aTåZDž2Ó‹Û  ªì©ÐÄiªMÈv qä¦ñG({Ä]àð 8G'É TiÆKÂ'ZG^³«\qE‰½,tç¿èP)£Å›ÿq[ݤ4 híqUvp/0Ž£öf£‘Õ{š¨ï?êöý¦+ðCEĘHÞM"è ™Wñ¨–AÚV)lt‘*Å5ï½¥z×É<¥T%¤ýxSC]ÿuœ6Œ¹Y¢ChéN-½€ØC=‘MfØäYBDE¦ÝQÅÈÉYj‚Ö²¨Êµˆ^^cT‘p0•ašRyAÂÀëþóøñãÇûûûû$¬ìäÉ“'y–OžL”GÃO¶qÙË;©CqEG¢RE'#6‹*$Gd”Y» Þ8Ä’[$(¢¼Ú÷C^lâ6Ÿà7GP¡P(ˆÚÕµF« @lù@¢Ên*d¤27v¯ò ˆë¿§-cÎR­V«2ohÖö³™^,&JDñ!ž›žè¡¨pëëëë2÷82«;¯o*hïÕ XVÛ^V3ÚkŒªÍÔëõz2™Lêj\eã/ SaÎÈJF9_³ ƒ‘!{þŽTå!£€}ñ²DU/ –D"‘xôèÑ#]+P½^¯§Óé4YO"Ë»¿‰ös¿^ÙË;©CÝî‚øCUF7Ž•E•W†§„SYÖ Þ8¹\.÷Ã?ü`â@퇨îß–öe´Z­Ößÿýßÿ½ì;ù|>o»‡ø!5ÛÙsšÉ¤À¹\.wïÞ½{¼¿µZ­Öµk×®]ÞÒ"k¾* @¤X’_ýS!®ÿ~œaÉ5£þuJlr² þtÝ\Þb•%ðSÎ……7‘ 8/‚,þ?Œú´42­,fTeñ…Ö‹E¢!k Z[ 3”û¹ŽHh¦÷7‘àí7æ^µÇ²^dA†„póæÍ›°Ç[·nÝòëê­JÆZyUî窳•*ÑŸëºîçŸþyXo¾ÛÚqëÖ­[ÿôOÿôO*ÃÛôôô´îþoBàʰJ¥R ¼ù©›…ž 2……×7™üâÕýâúïGÁi˘ ¿*‰2Íÿ¯eð§ã4EIšˆà/rqH$‰ QýE‘K$Ï­™ç’=Àé >²RäþD•Æ6N§wvvv¼¸Ä!ñ"—z™—DT¨\m#îùÂ&nÏ׬—Ê[ÆqÞÅ£ŽŽŽúiWd…§÷7Ñ Ýo5•r™Ý“t“¹"ˆ.Š/ª’›PTÉèØ\Cªd`KKKK¢¿«r!5ÆéÓ§OQ¾ND˜÷ocû<Êår¹R©T y)tö"øC~ö]ÄøEõÙ!µÝEÈâ³u³ÐÛˆLáäÅãÄ«û?Äõß„‚ÓL ÿŽC)ØR´ÐßÓÓÓ#ÀL&“yùòåKˆ+V:N¿xñâETå?t]+m)᥊Eñƒ—ò¶"¸l̬ ©ã'w`$&ÝŽ © ¨T*•¾¾¾>•5΋ˆFT€ÞßD_å®d× ×…J€ Û³ éLr¹\îÆ7dßÉf³ÙÛ·oß6ÑžlþÓkP% ¨”qív»}íÚµk¼ýZI$º[Û§ak£«Žßÿe÷É ü*·oYŒ¾WJ¥RI$¨AkÆÛŽ, €W^\&¿xõx“3!.®ÿ*‚þ‡RܹsçÎïÿûß“¤J*¡ŸJ¥R>|¨“}ÕkÜgبja"J\e¸uZ8 b“V "!üâÅ‹s¹\ާl3¥„ZE‚°† Ì™ÅÔYLr@ÜÐeÆ’Ô¹V«Õ « ûþmk_dÿW•¥¦2{U f2™ÌêêêªMñÝ~”ÔAµ¦datȉʪ– Qnz)ùç… Ç\¥èð›LR« K&“ÉŒ³Ÿ‹Å"½0ëõzýêÕ«WÙÉB[ED1”<Á”g!–“Ò|‚t»×Aå²e^ÜN±å™A€x˜Ð‰ÌÂè8ð¹í'.]ëŒe%áØ’4+/‘žL&“¦ËÿѨÖQ>Ë Ùè¥„Ä HVl–l6›]^^^öÛ¶ªDX¡P(ÈÎ2:k±Ýn·Mfn÷K˜÷ocû*Tû:NONNNÕ¾ãxóZ¶Õù/ld oú(3¤z©¾#2:Әʳõ˜OOOOË~óøRð„-‘±^¯×ïß¿ŸþÌk :ÞC\KWð ÚíŠÊe˼¸8";\Û–ù~tttTµ‘鼡^~=[dãhÛ'ÊÛ¡ƒJá8ïÖáööö6z <‚ô€ÌO‚é½\Öv"‘Hüò—¿ü¥è½–é< É¢Hfö;wîÜ‘¹<û ÐYtÿ‚H~©z_úõxàõýGÝ>„¥¥¥%™’"‘H$¦§§§MµÇ&ýój)ž…zû² ¼BW¶Py_š˜Ï¹\.÷ôéÓ§³›l®¥Óéô7nˆä÷Äõ?¨’¶9A7ü —Ëåés•/O`—¹áó,ô-²žðnTfEbKmllllèjRDVR¿Vš•••˜d•H÷+SÚxÍÉ` ²a'\›™™™aŸíÁÁÁ4E´™©6#™ÇdòȼËf³ÙƒƒƒÈ:„”Ç :^?dŠÄnŽ÷Ú 2?£:>ýôÓOEk ›çâˆkìÚÚÚ±ŒAÜÄïÝ»wÏV¥àc1Q{ž‡ÊH•N§Ó/^¼xaÚÚõýGݾŠ0½ØsëºîÄÄÄ„ßûeà hÊår9aTõÞô3ŸI¸’®¼${×ÊÞwPvhIS/%EРШÆ\•€º>2™Lfooo׆/O`Ynx–GÖD¤M¹¦«b‰ü Ò®„-HÊâg¼(#Tîaª< 6#K¸'wkQ]ÇQ'ùqœw!k}Ïd2™—/_¾ÔÉ!ƒ”`2q­nŶ¤”a¤${oP‡^•’NÆÿ#^P•üã%ŪT*•Z­VýÆoi@È{Šàº®ë5n]Ä>‘H$6666dÁÞÞÞžŽ•/êûº}/å)Ùõ1555å7C<N@S«Õj´ÕÞ4ª÷&™Ï‡‡‡‡*‘N’,Rf¨ðrvÂO¿D1æªP[Ò¦(7 É)#ó8ö¥iñVVVVh ŠÈŸçÂ;‰Jƒ4›ÍæÄÄÄ„Îa 2édñÿajJUU ¼(#Tyâ\ǽ¬­*í44Ék}‡†@i·Ûí¹¹¹9›ÊÒÄ ›’RFäêÕ rí 0ª7ŒÿG¼)ù'Ší†(à¼ærQ…Òú±4AÔ÷uû ÏÆoBXv}˜pgà hÂÈ+À«¨Æ¢ØÒ-ÊÃKÈIÜÂÝ¢sèÞÅó6>:::ZYYYQýÖ—ÀqÄñd´«;ÏõÀu]wnnnŽ}1‰´I¢Øæz½^¿pሪX,¿þúë¯Uß“ ’aZíTñÿ^”6& ƒ8X[[­VëÂ… TÚi•õÂÿøÇ?úù½ã¼[{SSSS~¯ÓɈBxPàƒ)³¼Uû\/¢°=ËøIl,x ‡Ú7nÜðꆭ²ð‚®=íGTíuÿQ·A•Âq¼+ƒHú' £4^dÜÂD×{-Ê9ç•(Æ<è½Ë· Ýn·¯]»vM§“®ëºW¯^½ÊtD‡'‘ùMOOO¨Ì)ÛWùl’d2™¤û"‹»Ój§ÊÖîU¨U݃éêaTÖÌ0(—Ëåžžž¨BÇOøK­V«ýÛ¿ýÛ¿yù-K¥R©ŒŒŒŒØê 0666&K°3666E¿Pà{$”ÄëP抴Bwo;‰?²’Žs톸a¯®®®zIrQ„…á ìÅSÔT»QÞÔíCR ÌžŸY¯H‚(7mY%Í¢þšÊ+¥Ýn·/_¾|Ù%€N@ÔsÎ+QŒy³ÙlY¦Ò·ÀqÞuòܹsç ±rŠ\ÓTÚh¢`ùƒƒƒƒ:Ú&QÜ}Ø™ReYý,¢NÍ S\Øên].—Ël¦W^5A¸¨Õëõz2™LÊ2âv+¢9§œAäP抴Bw¿ ÒCI¤Ó}/"ö *ùç8Žsÿþýû*o2ÈÓOi@U˜[ê6(tˆ8ˆj ^á=LäÐ¥Ýn·m8wéx½ÅÍýŸ&Š17idkµZ­¡¡¡!¢¨2¢pœŸæäÉ“'y‹Ùu]wdddbåe]ô£†"‹»óÐ.ËCà8þ,ˆª<ýýýý^®%"…‰MîÖ­V«Õ×××Ç+ñ¢K½^¯Ÿ>}ú4T#Y«ÕjAúé9Åb±DB]à‚"¨ èP†¢,"H4ȲZ­Vkiii r=ÈúËçóyQ²)?„™ø’6ù ¡3EÔ‰?£nßq`J`?J¨  JAeü‡BÎ]¦æt±X,z9'BçR\òqÉ{̉‘Ík{Ä ^ÇË8RHé^rÝŒ¬Hç“J¥RÛÛÛÛÝ:_d÷Çòyñ•ÆOmÉ„Ûmû‚Ø)sÕ­ïå¨ï?êö£DvïºÄí}-:w°@²Ö#0Âó\.—;<<<”µûs?ïðfjKÂd‘è-¶$‚„H)Óé/A™§#¨ ¢¾ÿ¨Û ¢÷4Ê1*-ž®pGoŠxïdÚ7|Ñ!AÀ*(Ùy&:€áÁANGvvëËcÔ÷uûqb1PW™›ŽÈ›yüQÍ|ƈiDsŽVð¼Rðà… H§£rQít¯¼¨ï?êöã*ÄrLÆóР€ýTÿû¹,î€bh~žÒ‚t2²ÐMBÜbªuˆúþ£n?n Ab$ñp¡Û 4¹tÿGLQðhuA¤Sðú^î”}0êûºýNH·ò‹¨; )…P­V«ª¸:ôööö¦R©TÜKS ï㺮«*/ƒ &I&“É3gΜéíííÍf³Yò¹ëºîÜÜÜî1‚t3‹‹‹‹Ý¼F}ÿQ·o¤„yÔý@D¿aè¼jºQƒ‹…ÎÞƒa(‚t^ÞËä‘õýGÝ>‚ ˆ@➎ŽPè#^^t(ü#A™—˜cAND÷½Üi®çQßÔí#‚ H è¼èÐÚŠ„‰( þ‚t2:ïåN´¸ŽäÄ1Ë=®#¤@YAb„èåK¬HIµK"‚ ?¡rÑ…â÷E-Ú/Â>¼Šöµ …²/šÞ7£z¾(¸D®£`ÖQTà:B:™NáAŽ¥4ðÒɰk˜> ‹ÑA0£>´Ý~žSQ=_\¢×Ñ;:ÁªˆëéTT¡iªõ H„tC ‚t*ìúåiÚ!kÜä‹Ytˆ úд°À»/™eÃDî”(Ÿ/ .Ñ‚ëèqÏA„ëéDtdÈ:GPP¹Ti厲mèƒ ñ„} ë&ó ê`É"‚ÜCD{˜)¡…w?~ª§@…—(Ÿ/ .уëè'ü¬£(Áu„t"ŸÏ˾“Íf³·oß¾íµ ˆÙöòù|*ðîïïï¿}ûö­ß¾DÅwß}÷ïóÝÝÝ]z&‰Doooox=C¾*÷¢8hwjµZíØŸ(—Ëåni;nÈ6ð8 ÿÙK9N§ïÞ½{7Š~EjOé—±\.—vǶ}ôŠÊÛ)¨fØng·‹ ˆ²óF6›Í.///GЭÿŸR©T ê|—Ëår÷îÝ»ùî7nø}ß½{÷n:Nóþ–H$ìÞ8000Ðßßßï§]›8þüyÞç½½½½‰D"Aþíº®»»»»^Ï_è–z @îa„ˆà a¹á‡Õ¶©:ÒL k²9÷Cf'ßHâI¶ç{¡ã(’yWg‘ÍEèu¼d6±O`»Ý®WD{‹ ŠDÓñÎÐxïnk×ó’¨ïWÖ§¨B(ƒì“h¿:<<<üì³Ï>ãí ~ÎÝ:òÝ{&Ð=ûE€9≖™`´J:NommmáÃFtH¥R©………Þœ+—ËåÙÙÙÙ(úe ™fÞ„6ÚvJ¥RigggG¤1—‘ÏçóqSd2™ÌÅ‹/ÒŸéÍf³££££A\›¥P(Dnœ"Òétzgggǫ Ûíìvý0999ÉÛ[ÖÖÖÖšÍf3ìþ ñ!“Édæççç£î‡Idç{÷îÝ û¼‘Éd2««««¼ó ÏÎÛ·oßf÷+×uÝ«W¯^}ðàÁ^(¦ˆ+W®\¡ï…öò­Õj5¶ƒƒƒƒ£££#ÖûAäBo#ív»ýäÉ“'ôgïÅûàƒ>ø€ü¿ëºîâââ"ù÷ØØØX±X,ª®Ñh4ëëëëºmGÉ;wî4ýQük?«ä`Ç  —Ëå=zôÈ‹ÕAü Šûï$០zuj>HRžNƒHZ­VëòåË—[­VËqçâÅ‹ƒRôymÇy÷,M)ròù|j)Æv;»]¿ õñ §¥7ÂÌ Šû7!ü‹}òäÉ“Q÷ͪ˜ôV«Õêëëëã*•J娱cÇD/[¡Ý‘7777É °Ùl6Ÿ={öÌqÞ…{LNNNÑ~ÿÀÀÀ€ÉkBÝdËårYW‰#K^ˆívv»~I¥R)^N ŒýGTˆâÆ;ÑyãÇü1ˆöDç;S¥œüñÇOŸ>}š¾öØØØä·¬P~ìØ±cüãÿh¢_ÝoN™|ƈ„ Ñ ”¥\.—EÚ¢ BE€]ÊPáëÖ­[·¼X[^½zõ*îšPÓ´Ûíö¿üË¿üKÔýð / > dÞÔëõúéÓ§O³‰gl$•J¥.]ºtÉqø‰oÉz Ê ˆDƒlÆc¢fwª8HÂôôô4ÏJŒívv»~yaÆiD†¬âP·Ñn·Û¯^½zĵƒ>ß™¾þý×ý—©k!ˆ-ýýýý²Cݱ‰"€-ÄèK’—lAΜ9s&™L&EoµZ­­­­-ȵÚívû7¿ùÍoÌõ.h„¶þêõz}sssÓq‚õ0™ P”aæþýû÷E®~Íf³yëÖ­[²ßó¼°ÝÎn×/"ãfœFT@CÓéþüyÙß¿ÿþûïÃêK¨¬ÿÚ ÈøhUþ(ªçQä´Ûíö“'OžÈ¾C×UÆv;¿]¿ðæ·Žõ?•J¥¾ýöÛo?úè£=zôÈë>KÊ'ôÑG}ûí·ß¢ÀnDÉ.ïß¿ÿ¿ÿû¿ÿ;Š>™‚VVVV ¿Íçóy¯B¶‹íÑ® è³·N¿£†î7Da‘H$d¬i#"W rÿ×qÑõB©T*xqÅ"7Û å4lêú/s åÑ­Ï»[ï[Åwß}÷]Ô}0ëŽüÍ7ß|#û>íà7>Úu]wŸý|xxxØ„òH%¸A9ªç͆-`»Ý®DI ¡Ö"ü“=9‘H$¼(ˆðOÞ•Ùl6‹J{%¼öãÉh …B¡`ʲI„6ˆ'"¶‹íÑ®_ˆµßëÙ;JüÈ „•••R“«ˆÒE·Z­Vgfffü^'›Íf_¾|ùÒ–z¿ÄõRj…¦[Ÿw·Þw·A[ÿFc}}}]ö}Ó^D† œÝÈüüü|†lÛ…BòjÅÑÚO`½ÒLrÜq‚Í| ÝDK¥RɤPDÈf³ÙåååeÓ×í& ®ÿ:%ÿºõywë}w#´@BçýÄi~½D1Ø~¯«ªäpxxxøæÍ›7k½}ûö-/TÛí¾vý ²þCoŸ|òÉ'ª„…2%€JøwœwJòk×®]C«²ˆ ºÆŒn$Èj5Ø.¶ëçÕjµztË«%Cd¬ ž÷Ï+ÍÇÇ\R(hâ¨u¹¯¯¯ÏK=zS1¯Ý dÖjµ´ä_Ï›lDºÈƒ/6çywA $:ISÛív›Þ7ýîÏKKKK¼zì~®« Ûßßßûöí[ȵÚív[eM& é°ÝÎn×+~­ÿŽó®Ÿ—/_¾ìE þ‡†††:©ÂIœ‘6tŒq¤Ñh4Nž{öìû¹Éäk%¢²ÅºÕм(Pø'"×cF\` ƒƒƒƒªuÑl6›====²3GÿÀÀÀ¶‹í†Ù._k­V«‘þÛ´îE¹IXÊårYÖw";;vìX­V«Ñ;®Jüyùª’ A“ÊÍÎÎβ7GÃvggggeZ*"a†dœ¤-ÂQ¶­ƒ¨..ëºîÄÄÄô`ÕóŽšn½ïnÅ«õŸ@{˜Pìð’ ùqµók±Õ…d¤Çv;»]/d2™Ìøøø8û¹Å›ãÀ•««««Ÿ}öÙg(üÇÑœÑ)*•Jr^!ò syBYlÛ ¢]Çy¿ü k?­¸Óý}@BóËårY'éúØØØíq|lllŒ,UîD›2999)*#¡kY¦Kh±¤ÓéôÐÐÐôZ^!B7$TÔÑhÙÒ¶¨..nÉ¿N{ÞPl¹o¶n+NÂN…?AþÍ&%¨2™®¬¬¬xµ¼Ûmý÷S2Õ¤}-š “B–”Ãv±]ÑÞúäÉ“'^cí!J€t:þíoû[þã‡èl£{žAü)ï°]lW§]âáéµü q“÷£¸ Uˆ¦nÅ5B³Ùlï nÀ0‘M.ÝrƒªlÃAä:`]ŽEL”Þ ³mˆë¿—¹qÞ^‰ú¾!g"‘HlllléU™ ‚~Ë«펼¶¶¶æõEC'ðóë J†bA‚BdÉmµZ­¥¥¥%?×n·Ûík×®]SÅ«Š@áßNDg/çA‚‡œ_½”Á³ÕÅ_†ÊcØ”§R¤ ÕMêZ TÙ†{{{{ýZ»XWtQ– IDAT¡2!Éô¢­‰¢m¨ë¿nÜ8>oDyßÄ⯻q¦ÓéôÎÎÎÎÑÑÑQµZ­ò¾Ó %VLC $&„ÚMίÀãdzž#˜7‰3"ë¿ÅM³Ùl é*Pø·ÙÙÆK¨V\¡¦©PY[uÎYØ.¶D»,´§ºáÚ:…ª 4¿žŠHª›œ™™™L$B‚›=^Gè&x€Qµ qýŸšššÒu•‹òy³¡.2D1ö:á/t2”(ï[z%ŸÏçyýð[b¥¡ÇÛ„Òl6›kkkkŽãß‹¨^¯×7777ÙÏ1oGR©TêÒ¥K—ØÏM(Þht•(üÛ‹è}؉‰ÿXèÑ0ßÝØ.¶®ëº####ìù;ލªðìîî¿ãŽó~¬®J+£*…æ7Ž8HN:uêĉ'dßa…nH‚ ’…Ñ´ÀfÛˆë\^–ç݉të};N0[H FÇ1oý'Ð^ãããã~„uQ2À¨óeØgˆíÆ£]Q’$SÖ¨…{•áÕÍÁ7ˆ`Ø!zØ.¶DJ&“ÉNÉß¡JÂË;Çyá¸ãÀ² BÙÜÜÜ„>•–# LÝ^]û£l›ÄõßÏËÒ†çÝzß݆ië?öð“¹ßqgkkk‹'ÄèæËøî»ï¾óÚ/0l·³Û…"*‘亮ûøñãÇæzöŽL&“ÙÚÚÚRyS¥ÓéôÖÖÖzÔØE*•J-,,,ðÞ÷nݺթ è¼Åv±Ý8´ËƒxÐêä­BÞqÜqÌ%K‹S "|C„n’9R&t‹ê—J¥’©¶GFFFnܸqCä}!kÄõ¿“_–Ý€,Œjé–]›.»Õn·Ûƒƒƒƒl;²ú¯ŽóóõÆÃ6ÖÙ´EÇïûÉÜO+hüæ@0-t PR©TêáÇ¡‡]T؇h¾x-tç-¶‹íÚÜ® HÞ*ä}ŽÓ%«üÄË7(d.¬¢6 …BA'ÉšnÛD¹¯!±ííím¨ P*•Jâúü’BU¯×ëÉd2 ÒYŠÅb1îqW~h·ÛmZùiºÅôôô4Yû~Ê›9Ž8 N~ÝÝÝ]™²H' %•J¥z{{{eß! l·³Û…^Odý7m€H¥R©o¿ýö[]ITÄUH+A·t­ ˆIÏ^lÛº]è¼U^ ¢ÝÀqú`éSq a@»Âê ü4¹\.wïÞ½{Q´­"›Íføá‡T–=QlM£Ñh|òÉ'Ÿ˜ê$1Šnn:–^”±Õjµúúúúl´ÈGÁúúú:©nÒ¢N+iMäxþüùóׯ_¿f?÷ãYÀ’L&“gΜ9ùî‰'Nœ:uê¶‹íBËúïUø'ئp]×ÝÝÝÝÅv;ú~EJ2lâh–¾¾¾>hLlÛ ¢]rÕ5L9ÎOQ/ùéDžÝAQ( º÷ç‡ã¼ìèÅb±(û‘èÁšØxw¿ôôôô÷õf³ÙìéééñÓïL&“Y]]]ÕU ˜hJ"‘H¬®®®Š!²Ø8‚—’^òyÛL÷ýÅ_|á÷P,ª —ñ ‹v»Ý~òäÉÇ1[^VÒšÈ-@÷“&›Ífÿê¯þê¯ ×xóæÍ›ÃÃÃCÑ߉DBeõ%¨ªdÐ[l·³Û…Àó®1mý‡ÿ­V«õùçŸ.ó”P)TãÝÖnTDu¿¥9ÇûMíb»A·K Š/᪉D"±±±±qt¤çÝÉp˪ÜÿLÕWWµc3QÄÁ¨ÊЉ7²,ß·oß¾­²pÜ¿ÿ¾ ËJœŸ·ºõ¾»:c¿ /ÓÖÝOšÑÑÑQÈï›ÍfóÙ³gÏdßQe²%¨’d¾yóæ ¶Ûùíª…,š´þC„×u݉‰‰‰<¸zõêU?J“tR•›Û èýª”nAytb»ØnžÂt¸ªÈCUúcC¸‹*$úîUÁUˆ\D ýýýý~WiGmÞÜYÁù«¯¾úЏÿš†h½T1Ö•J¥"ÒŒñ& ÔõÿÎ;w¼õü}âü¼ýЭ÷ÝÐIöLx˜¶þÓýä x§N:õhRen‡ÎkÕËìÙ³gÏèûÆv;»]¢ESá‡PáÿêÕ«W‰Â¡^¯×!J€‡>d 'oß¾}»¿¿¿/úޱE•kV´t[»QÕýª”n:ydTB ¶‹íÑ® â¡ê5‰5/ l|*£¡©pM®@ä"Jðs°™™™!špÕæxñâÅ‹¶ÄÏѰIÿjµZíÖ­[·¢ìA¥¼!@\ÿ[­VëÚµk×L¹þÇõyû¥[ï»S µÍ,¼S^™L&sñâÅ‹ŽcÖúOð+4©´ÔyÍVOàÁ ¢Øng·+Bdý7•ÉÝ‹ðO€(²ÙlöÛo¿ýVçà¦clQ¹çîïïï¿}ûö-¶kq¸ß+W®\‰¢Œ1¶‹íêà×+Àq~Jæµ"@e44e„ç*‡Ÿ)š&ŸÏçu³+V«Õ*-8« 2×õ¨`“þÙ–÷’àÕF†¸þ›.ù—ç-Šy÷šå>.÷˜õÐÉ®O3999IBŒt¬¤P褅^ “™òH§ÓéÉÉÉIÙ5T…y{¶ÛÙíŠYÿý*©ýÿ/J“Æz¿à±»»»KÆ©ÛÚŠ¸ßo&“ÉŒ›¼&¶‹íÝ®_¯€¨Q…à™Ì1Å%•J¥¶···UY !J€L&“ÙÛÛÛ;:::ÚÛÛÛ£­¹\.wxxx(º>û}/är¹ÜÓ§OŸúu™ ïƒí›h¼Â.AQ­V«ª>¨Æ<ȸ˜8=o“të}óPeVZûjÙž§û{YöZÙ> Éz«“å–—8Gõ{YØýÚ&¶ÛùíB¯e2™“ßµDíõ¼þ …‚lŒŽŽÔç/×è¶výàçÅýªÎÐuy?Ð×Âv±Ý Úõ Tž%Øp jËd2PÙzHŽŽÄ/GÞdá†yB+äú*èIæw2±ˆí“ Þ¢bï[w!˜€]LqxÞAÐM÷ö<³aÃf¡Ÿ·î>@?S™2ίÐQxs ú{/B€ì¹b»Ý.‹èÀhzÝóÖ“—}™7n¢½¢(9:⟠û,ﺭ]?øQDq¿6Ukª¦ç5¶‹íÑ®I ï&Γ&ðr&ÿF%´èÂÛä ‡ Ȧ.ê¯ßÉÄ^— Q+x÷íç…bv¬¢xÞ¦ç± ?ÂR”óܨxÿyëx@­ÿŽcÆj ]¢¹ÔÚRÍel·³Û%ˆ&¼¦xÐkÊ I¯ÿ¨Æ˜ R"v[»^ñëµöýBß¿¼q‚*,ôÜÆv±Ý Ú Ù½Ørž„*Òe}æ;X.Õ}h*D/mWT/ø™LìCàMî(Pá_ÖÏ áM̰Ÿ· €(î;*Pð/^ô~£:ÈšP@_2~-˜:@úŽívv»Ñžä»5•J¥ž>}úÔ¯–¥ã‰ã¿Ò®Wü*¢¸ß Ïö½€íb»A´$ìȦód²‹Ö»Óäæ•`êu2±÷.ºN /q‘¶(Â~Þ¶(lç¦AÀ;t¬€ŽóóqSÝ— TÀ“õ_GS Á‹²Ûí¼vöþGIPk•±ÛÚõ‚_@÷Ö;˜}/`»Øní†yߨtž b¼µåRSJÙ‹;ȉåu2™Ü´MN*Ñóðc1 Ñ}‡ù¼mQ„}ßQ €Ÿ çžªŸ´ åEa@cÂÊJ£ê©½R÷ívn»QXÿ£Äô{ ú>è¶vu1¡ˆâ~MœÛ¿øâ‹/d b^°]l7ˆv»ÓçiOïO®€Í}šÒNRø9Ù¤bŒ 6+¼ï¨@ÀO@…z]ë?ï74¦2—CúλW/xì°ÝÎkW4·;ÑúOcê]¥«àé¶vu0¥ˆâ~ýk…B¡ :÷‹Þ Ø.¶D»ÝŒ©½Ã—Ýëƒ×}q›ºY?ÂM Ùazm„ Ÿ· €0î;*PðPÁ^×úÏ»6Žò¼LôI„ ¡Ûí¬vEʆNµþÓø1¶øI\ØmíB1©pœðïWwÍÒëÕ †íb»A´ÛÍø5Ä}ª6FS›3TH »ü‹~„‘Ò¥S­!Q=ï¨éÖûît ½N¨@\PYŒƒæ°Ýx·+:Xvã¾5¸˜Þ3º­Ý¨û~egÚ ….lÛ ¢Ýn¦ÛöJkXL¼D +H—7AÌAï8ÝRŽüÌËÔ”ëý«zUõªëû9Ùtw½úóêûÞ÷ÿÍ›øÃþð¿ÿû¿ÿôùÀ‚5“¥¨¹h("›Ífý:g8±SæiE›PÁP£¢ªªzxxxh­Ü|~~~®išfþ®¦iÚùùù9Ty€ÂÓËÙÿ€P,‹N”ÿb±X úÜà€7<ÿbì " àvÐ ˆ-¸ô D ZØŸl~ÒÆäêêêªZ­VWà6=@TÐu]Çãñ.âñx\×u=èsÀa±ö㪋2VX[YQé˜dd°ZGqÖT»ê̸AÚ–9²yËYÑu]ðàÁƒv»Ý¶~‹Åbëëëë2´øÙÞÞÞN&“I»Ïr¹\®R©Tœ;›ÍfÓétšôd2™\YYYq:F”¡WhïZXXŒ0´wU¶EÈ˨–°÷>¬ïiŽX §a1 ‚±ˆ¤=²W{5Ò{uñö®N[ã„õ¦‘”7JˆHJ[à M3ãöE”uSK‹®1œ8»ß°»kwC2™LNNNNŠ<&Žl6›¥ÍS;œv² }/ŸÏçÍ ÷ãÇ'‰úüììì¬Ùl6yÆâzÿñj]h6›Í ë÷c±XlqqqÑéù†šÂ´‘ZQennnÎ,?;;;;†aAœ ::Ñ =n¬78…ß!Üý±0â®ÑzlÖï±t€¨è„“ùÀROCä5+ þ¾áòú\p÷÷üüüüÏÿüÏÿ÷üY¼Ž¬òŠön°‡'€õÅ]€“ùëæ\qã²FÌXá•[A]/Dˆ{F´gîǺQ?À;ï‚ Åï?ÀCTc/®"Ž`)ÇK"‘HÌÍÍ͉<&+Éd2ytttäDPã"üô~‹Å¢ÝóáùWUU]___·z!K¥RÉzl]×õùùùyë1ÖÖÖÖ´ªªª¾xñâ…ˆè7 …B­V«±|·^¯×»»»»{{{{Ýx Ã0ëõzÝü÷X,{öìÙ3¿6 š¦iVJ£Ñh ¹m¯•Íf³ù|>oý{­V«õöööþ×ý׌ŒŒ”J¥’õ;ét:-j~øðáCÜgªªª}}}}"Ʊ2:::êå;‰»¿Nà½ß}ôÑGèßív»½´´´Ôüž‘‘‘\¤‹™z½^ßÛÛÛc3ÈëüÁ¯u¢~0üðî3Qtœßàý ãaµ€ã,"$ë Ɋ⇋*â^oäDzÝí°ó˜Ð®Éî7a*€äÅ\ã±òx™¼´<ÚÝ?¼P8k«¨kµ³J“®ËÉ;Æ:‡H"ž¢›¼æßpÀRT‘ÖyÇZÉßI‘@A^/m|ˆø1n"ü\¢à´¨´¿Œhàý{  Ã`Ù”Ò*nRž_7Š³Ý½ñrÁÆMJQÊ¿Ýõ°n¸ì6Z2äéѵù°Â" œŽíåF‡WYv nã+êíæ4˳ÁnN³Ê+Ò½ä )wbp[„Љ‘× Nžoš‚“…<Èëõ‹¨‚XpçÚÉÑ!¢ï³{Ü9wòsÀÐaÐ(ëÀ=DÜ„pûÐY½4¢=Õªªªwîܹ#ò˜ˆ;wîÜñrÒ‰>þÏ~ö³Ÿ‰:–ðxòx7^O/~W¼œÓþ=^[Qü›Ó< “™‡;Ï|b‘›¼Þ®Ö¿lFĨ\o” A¬ ¤q;Õ³t$pò¶SŸð€ƒ¶€²>PÜÄÀ-è¢:m#‚;º°z\Y7åæã9 —6Eš»0oå‚Ga²“y¼›a'ÜØfx ´wˆ$«Y®Y¶…6j×KÃm‘=+2‚Xhcw¢w9Œ‘4¼ûXˆ`ð†Åb±(£ùú믿&}Ž6¨år¹ì×9™YXXXh4 ÆäEÓ4mzzzšö=žÂŠ…B¡0111kç§(×E÷–———ÍCýººººÌc‘æn"‘H<~üø1ËyÁÑn·Ûæ¿Ùä»}ûöíx<7ÿíÛo¿ýVôùˆ,Èòmmmmá 8Ú½ VXZ'úEÔ®7е. 6777ídþôôôt'|S©Tj|||œö½R©Tê2ÁRÐÓ«¶§ªªªvEÛívÛ®ˆ#àHÑDF¸b±X§ЩÜP”ëjÁ^Z=D‚Zä¾¾¾>/-)ïß¿oÝ€›‰ÇãñÛ·oßöj|@NÆÆÆÆìªøš©Õj5Þ® ¸ÎfF#—Ë庺ººFFFFìúÓ†t:ö²ø£[Ë-Œ{Íñññ±ùÿ‰D"1666fþÛýû÷ï[;oX'»±B{‡Fcsss“tŒ½½½=k 3±X,vÿþýû¼çæÅóÚõF‘ ×E¹–ù;;;;Ö¿Ù5É ­òÎL»ÝnOLLL …‚ùï•J¥ÒßßßOsê,...ŠÞWNNNNÚíe_½zõÊi—ž2:ýÄ|¯ìЉD"qrrrruÉ`]ð/Ú“ÿ¸éµPpBÇSÍÚIa)Þ—Ûɱܿ§9}±`\Æõ:5äïþîïþNÔÄ«('÷^ÄÂÚ‰ãâžQ«Õj}ñÅ_Xå¡yÞÚ…ñŸŸŸŸöÙgŸáÎËi ÀÕyÍàI ­ n ˆ:9Ž×ó*j×+âZ– úàg-€ Ö_–1iÇ¥)Î^(€"ºäˆª{cëû+ZñÔ>VÔ¹°Îc/êvØaÞk¸Ýÿ²î‘ÌÇâI`5⹑I7pÈJ¥RÏž={F²î*Ê¡]8ï§_´Z­ÖéééiPãþ£iš6:::JúÎëׯ_»éçûÕW_}%j^ÏÌÌÌ༆±X,¶¾¾¾î¤’ùÉÉÉ ÍÛe7Þþþþ¾Ó¨¨«(ŠòÝwß}×jµZæ¿Ý½{÷.:ÞÍ›7oöôôô˜?¿¸¸¸øŸÿùŸÿq2Â.ý@Qe|||\ÄZñÑG}Dúü›o¾ù†å8´42–´?žoÔ®7jȲ.†a¼~ýúµõï"£w‚DæH¸”ï6›Í:ygí@û~YІŠÄ¬ðåóù¼ˆc–Ëå2:f'{½a]øííímRº¢\GuÅãñ8o$O±X,²¦·£{ìdþa Öƒ9ÁUUU××××qÊ¿9×ÙÚå´Eýââââýû÷ïý8@ìò­­üþ÷¿ÿ½_çƒÀY±////‰D—CšL&“++++,c kh:N»9×d2™<:::bõHEm\3vzs®·Ý†øåË—/////Ýœ³¢(Ê—_~ù¥uވس(K4EqvvvFÊ&¥iùõ|£v½QD¦uacccÃnŽxÚî7>|Hú|ggg‡fdi6›MZÞ½ÙÈê\ÊBû3år¹v#€ÕCëV¶ÑÈçóy4V§DRÀº`OµZ­ÒîI­V«e2™ ï±s¹\Ή*ŸÏçyßYªÀ:ÀÕ•ÿ•Âq9RŠr­üß»wïžßÞþ¹¹¹9’5ìåË—/ƒŒ@ü§¯¯¯¡Òh4~œ‹9Ôˆ$LzzzzHç<;;;Ë õâÅ‹4k(+‰D"±»»»ËÒ«=JãÚaõÇb±X___Ÿ¢ØoˆY•IÿýßÿýßvµDnŽíh·Ûí³³³3–ïžžžžZ#$̘ï•™žoÔ®·‘i]Ðu]õêÕ+ëß½*p窪ªvsÑn·ÛÏŸ?Îr,š!­§§§çæÍ›7œ§œ÷¿^¯×÷öööÜß-kkkk´}>)¤Ù­žýžß˜Ï‡V'ÌKP$…5¬;LÀº`O±X,z¥ü'‰ÄçŸþ¹Ós{òäÉž(L.Â\èœe·Ýn·—–––üT´‘€!)U,¡f@çññÇLúܯ¨b±X‹ÅbvŠÍ¬¬¬¬ˆ^h“Édr{{{Æ%óüùóçÖÍêÇÚmˆE*Íf³ùòåË—Ö¿»U$hÞRžÔ*Z¡V~>ߨ]o‘e]@tb€]º“ž÷èÍ›7oH…RExÆyÿ7666dp‘ D¢üdÒ>ì'ð(Á¼¾Ìõ=Dì{ÌFWód2™¼¼¼¼ ›1Ö…)‹EšwÞ©ò¯(t‡ –ýº™…B¡ÐÕÕÕU«ÕjNô:*€dÙgoNwÀA0ív»ýèÑ£Gnòù€pBËå=;;;óza¯V«UQùmRnw*•JÍÎÎÎÒŽ: XÚW‘ºDm\v^ßÑÑÑÑ¿ýÛ¿ýÛóßE+¸Öb´P\4o)Ï54›Í&Í{nUÎü~¾Q»Þ("ú`¦£h†4‘ï.’†¯½ÿæ´X,]¬]ÈÆÛ~Û¬ãÂãѱY<÷f¥ßÒ¤(×Jº/æœíJ¥RAïîîî&Õ‡`Á|dn/ë‡d³Ù¬—Ê¿Öû[*•J´ßðÔbú¿€L&“q3ÉÍÖ>‘—fÙ•‰z½^¿uëÖ-RÁÃ0ŒÞÞÞ^œPféGëןqI°ór Kh’HVEZÛ¥F£Ñèïïï·¶·Òu]¿uëÖ-š¬Áy¥¢6.»:ñx<þña=OÑŠ®¨K±9Y‘íùzMÔ®WF¼^ìÀ嘻1Þ™‘mýõÛÈBî=«¢8÷þ£÷Ý_–"Øè‘” sME¡§¾²`çq®™«¢»UúQ;Ht¿X”¶f³Ù5‡YÚ õù½.¸½NTü†ÓâðÙl6K3z‰TþÑü´ÞßB¡P Ý žZL¤˜'9‹5ÇTÓëpYª×"ëaЀà å z «ÅÖ)vJ΋ EÃ4›ÍæÔÔÔÉ aÝ€Dq\vu~õ«_ýÊú=/ KÙÓMoqZ¸´hÌÞÙ žoÔ®7j½.àÀU»ÕÉÃoh‘4¼xi”Ñ4M›žžž¶þÝ©÷¿R©Tz{{{Fâ"¹E|×§ðTEÇóò;EdtŠ>–Á3ëÂñ[ùWEÙÚÚÚÂÍÏJ¥R¡Eì³Öb"ÖÐu]Çã™ú2 IDATñ®.¶Ð+æp§›–×ÈR¨ÄkÂÒø‹¨lVh]2V/‚åݶ3¶á¼–VF†a;;;;¸Ïí¢¢6. –yÅSLŽœ"áu1@Vx6ò²>_¢v½€Wë \µ{x^l¸1Üá¼è²ަ¥°ÈŠ/¿SDG ¬ ?J¥ROž UÒÅažg¤.ˆR©TÂy лM[´¬á¡¤A»ÝnÓÚ)!HE¹öɨKãààà€–Ëy|||üæÍ›7¬ÇdW 0L~„¬Ï×+¢v½aÃíº@£Ó¢H‘faÎûÖÂѤZN#ˆNŠÖŠöò;Etí?u᚟ÿüç?öìÙ3’1¤T*•D–^¿~ýšÍC+TÊZ§„» €ˆ^h¡«‰D"ñöíÛ·2,Zår¹Ü)=@p@ËÛ¬ÕjµB¡P ‡¦Hš6­9ÂI‹ò1+“QWÄñÅÛX»Na±ð#d~¾^µë>¢œã4jçýßÙÙÙY8Ú\$-÷ÞNQcI{uAœËårNÒd1ô„X®‰Åb±ßüæ7¿¡)ÿ,ûj^XR5qŽ3,QJŽÚ"Ì/¸—!/8Kµ”Cã¦-¡5dÈk^:Nƒð–þǬ[šÀ6÷?¦U]æQ8yª.Gm\ÖãÑ/òÿ¸ªâa*(óóõ‚¨]/`O§Dœ‘œQ2„±«ªªÞ½{÷®õÿ"‹äá@{d^¯'Š2ìêÂw3§(Z‹Ÿ±‚º’]][eßü,XºÈ¬ lx±Ã“ªI3B²ÈŸÏ{Y|E@ÐZ¨„m¼Ç‹‚_ÃÃÃÃÖvofx,¶4mîL+ºÄk‰gý~ÔÆe…´x•ÿoW 0è"­¬ÙŸ/+Q»ÞNÀïBfpÆCYŠ¿ÉˆSyŠKÕsëýwÚšÏ Ÿ}öÙgnÃêQÇ1+n Úa®²uuuåÆ9HCUUõðððådzðXØH$‰Ç?}\žÖö4£'  æ#/Z‘麮ÏÏÏϳ|7™L&ŽŽŽ¼´þ†aŸÏóœ¯Ù€ó¶£4bZW+æ÷DDË=ó\±7ßR×@Nòù|ž4§‚Šr¤É)ˆsx“pU=q KÁA‘PÀAóæADÚBë•ç2jãz Î —L&“ý×ý×,Ç …µ‰ÜÈ{Y‘õùFízƒBD¸¤S².ØEð(JxöO´nQ¬õHîß¿—Vç¤7½Ý¸n½ÿ,Æ>¤xŠŒÃµ1äÁìm7#"Ø 'º 9+¥R©äwžŒÈ¾.ÐÈår¹ú§ú'RúX,[___!#y"þDðÀœçÂk‰C ÉïGˆK&“ÉÐ^r™ûMË&“·( ‘íxrE·]bý~ÔÆ ¸PâÉÉÉI–߆a¼~ýú5é;¬^Z} æsÔ®·Sà¹Þ Ö'àj%“É$ëû$´4–šLš¦iÓÓÓÓ¸ÏyÃÿS©Tj|||Üúw7ÞE¡ûêõz}fffÆéñ;sǯf¥ß‹þð~ëÂär¹r\Ó꽉Š4ç‰øQÿëÿ (§Çiž‹ÙÛï÷䟙™™!Y~¡¥ à,›y¿ Còä—Ò¼ æ ¥"«.Ó fÏeÔÆ ¸w §§§‡Õ»C[¸X74ÅÙ¼™òùFízƒ€V$NäõZ‹Äɸ.àØØØØ°SÀ3@* [:ÉÃíÄk¿¸¸¸h'÷xÓ¬Ð6ÿ/_¾|Éj."”8 ˜¢ÒhPjs˜•~3Q[pØÛÂÂÂ)UfvvvÖm*OÄŸˆô”HñçÍéAøéíÇÑl6›n婆 ƒåÚÂ.ºh.ïÁýBÛ˜«ÉÓ6µ<ãÒ æ6vQ7Œ¸ÝÜÒÂY”%UUÕ»wïÞ%}Ǭxù|£v½2"òzíZ9ù½.8%ìQ†aËËË˸ÏI›tš÷kkk‹Çkóþ×ëõúÞÞÞëq¼†”ò@Ã\ÔΊÛJý^Þ§B¡P@ç™Ëår¬¿k·Û퉉‰ ôÛNkÛë\„¨T’e½gI¯pÔ Ho¿Lhš¦ŽŽŽ’¾#{Ž R?v„Èþ¡´>Ó<Ñ/8oÂê©$y.c±XŒµH-¯ÐÚÆ.jㆠZ.. Ú;ÄâÉ£Uȶë¿ÔóÚõ-<Üëëõ{]pCØ£*•JîÛ¤ÓZêÕëõúêêê*ÏyàÖÓ ßÍèÕ µ3Wêçõ¸çr¹œ_ó*•J…d´0ë;ñx<î&u# „q]ð+Õ`uuu•´·‘ ÀbðÀ}Î]Áe@/=´¯`ëÁ*sˆ à 4+!¢\.—EmöhÞ%–VN8oÂNÀB°TG¥m@ìÂ.£6nØ )[4XÞ!š'ommmô{;ïCPÏ7j×4c|:N‹Å"é;Ùl6K‹–´Û@±.8%ìQŠBN M&“É/^¼0¶···q´F£Ñ˜šššâQVpÎ!Q^mZ®6káFÞ¢~ÈÎ[ØÎìqÇg¼,Î 2Z ñ …B!júŽŒëm¾û•BÕl6›¤‚€Šâ>€ÅàOsÖ1GW 枘"¡‹Áb±X¤-ˆæÂ…´ãU«ÕªÛk¥;âððð×bnn·hmƒâWܸæî4pýqQKÜïÌïËü'ÍE–ßãZ÷Þ'Òõ¹=兀‹Ï\&Í}4ïIçÊ26mþ’®™ç÷Ö÷”ö^#pkJPó*j×KƒuÀ«\¶»f–k½º"¿~¯ nÀÍ+Ü: êøë>é™±ÊYžþõfpóWÔóa™K´±¼ÜÇþàÕ{$ÛºÀ2ßyßU7º íþàä$ë@ŸeÍg~î8! JÐóŽ‹0HçÄrCÍ7“öàDbÀ<b‘cøþŒk…uc.j\–ñ섊ÓßñÎkëù:ý]ÔÆ «€tîVœ[œBÚ|5¯¼¼^ÒÆ(Èë%á…ÀË{Œ í›ü^œ‚{ÿiï~Pë/«2‹`5à =gÜžÔ©1çù°œ'!Êî^ïóu‹Óy%jÜ ÷±²­ ¬ó†y.»185Àñ¾{Öc°ÌGž½áRÌ.dì]i×?”¥U¡Û–+N­Ð à/¤³nlÀº±õs]pΨ̒Up¹ÏF£±¹¹¹)r,/Þ[@ȶ.Z|íþ8éìrü=NωWß½Q( 2zûEÁÛºÅ-õz½>888¥¢!€=Íf³yïÞ½{^Q»èÃ0Œ±±±1QB’Õ%ú:aÜ΀µð‰J¥RÕ³¹V«ÕXºÖù|E^/ëf+jó¹P(D{áy6¶~¯ N(~üðþ#X ’±ðùçŸ.“bÈlë‚r7°´ŸçíìÒn·ÛÿüÏÿüÏNÏi~~~žGßuÔ0,är¹œ_m QS¿Z—á Ùl6GFFFDmêiíq Ã0Ý m^CºN·k·³ Uf¡P(xz5ÛQ*•J<†î Ÿ¯ˆë­Õj5žµ(jó9“ÉdDxïµ¢ø¿.8ÅãWÕí0ëÍí…÷¡ëºþàÁƒNel.—ËÉ–¦ȃlëB¥R©ô÷÷÷Ëb°bIéâM(—Ëe'ëB©T*ñ¾Ëi@ʸׂ ¥N@kD€F¡P(twww;UÌK¥R‰µ=ÚN”TÄéf2“Édœh·3yóæÍ7!mˆJ¥Rqòþ µÀ©!8¨çëôzÝ¥ùìôZN[¢™ñs]p.Ї¥UUÔ˜œœœ´k%è…÷ߌ®ëú­[·nñÌ!¿öÈ@g Óº`†ÑÛÛÛÛÕÕÕåÖP.Z]'©4¨çë¤z¼ù¥ùìugü^Üœ“ȪöaÇi×/ ½³2TôÂO”Ö… Á­ýAȆ¥5’ßÊ"Йà6ü°Ñ¿·I‡÷aÈè1:ˆÀ#“÷€‡%tŒ€[ ÀÜûïž@ªÐjµZŸ~úé§û ¸§èF9 ¼ÿ‚‡Š~@bîö!ÈС d/[ÕH‚¹‡;¢X,i¿Ó4M;???7ÿ®ÕjµR©TÊúÝl6›µŽqxxx¨ªªêÕù`ÁNÉ&)èv =Nù'ýæüüü\Ó4Íîûv†«««+è5QUU=<<E;´T4œa•¶Ó ²8ùêôw ਖ"Q^*È,ãC[*@h…Û¼P>i‹3,zá…Eþ¹-΄Œ÷k̳³³³v»Ý6ÿmmmm w¿4MÓÖÖÖÖÌk·Ûí³³³37çxͰ²Ï9~È 1777g5Æ*Š¢¼~ýúµaïïÎÎÎΚÍfSôyÊÍø"óí¤s/Nä ûºÀòl]én&[%‡§2µ\¨fT"Ü\g˜®•¥8š^mXxï»[£•SaËÊLº–0Ì‘nß'ïóv:.ÏüBÆ1¦Ý½´›Ç8ãi³þÃÓCÄü1CzÞò¬Eɧωó=§í XÓxhÇAó(Jï~¤ûKz–¤ß™e€ r20\ã§ÀËõÁ‹=áHhçóù¼Ó-—Ëå«+gJN±X,^^^^&“ɤ“±óù|"œ‹ÅbûûûûhòtÚ½L$‰“““t}ns{Ñ ŸN§Ó<¿K§Ói§Êt±X,žœœœØY¸iäóùÈuecccÃ`•sWWWWûûûû±X,fþ^»Ýn/---EÅk$;Õjµj÷œX(—Ëe}xü=À ΋ßn·ÛÏŸ?îäwÉ„ ³ñÂ\>999±Óu~dÈf³Y§‹«ét:ͺYCêfƒˆ'N§E(ʲ’ÏçóN š¦iNqE¹VBž={öŒÇP,‹nß‘D"‘xûöí[Ù=ù~ƒdP¹\.5¶×ò/亢(Š¢ëº¾µµµåd¬­­­-]×u'¿ÄÞ·9PR?ö^€lhš¦MOOOÛ}ÖjµZ§§§§¼¿;>>>~óæÍ‘ç ^¡ªªúâÅ‹NáVòù|ÞªÇýŸ •J¥žßI!mVÒétšÇ;¤ªªº»»»ëTùGÄb±ØÓ§OŸ²Œ›Íf³¢6i<ãFMÓ´££££ d_ò/äºb¦P(r¹\Žç7¹\.W( ^ÀÎööö¶¨÷#‘H$vwww;u-ãö^€Œà¼øŠBÎÿ'ý.JùÿŠ¢(ÏŸ?n|¢M>ŸÏ›Ó`n(Êõ"°¾¾¾.ÊCËÈÅG&“Ét¨Õj5/Çï’Édr{{{;èóð ÏøÊÊÊŠÈ éãÇ“¾cWœÌq£€›HŽ\.—³Ê’R©Tbý½_ð d|Ð늕J¥RéêêꚘ˜˜ÀmŒÚív{bbb¢««««R©Tü>GàC²ÙlVt*N2™L®¬¬¬ˆÈuè4MÓFGGGqŸ7ÆÐÐÐuj†ÑÛÛÛKŠì3o„¢†_²ð‡l6›å- \.—ˬµT>úè£HŸïìììØ…åóþ޶×ÁåÿÓ~Tþ?­]¡×†a,///Û}‹ÅbëëëëaÝË[eÚ›:Ù"㙵VÑììì¬,{Y³Îd2»ï躮ߺuëIŽÅb±û÷ïßWE¹1<<<<000@¸T*•z{{{qÙìQá œœœ$Y ëõz}pppåBÊlñÐ,~¬Ï. '«7¼«‹Ï²ˆ‹t -4hlë Ê’ƒ<:::jy0666FZØi…ÊVWWWI××)aê$ÁbÌñŠÅÅÅE–±s¹\giGŠi3„Œr]:šì[^^^&ípï…y#5ü’=$p^,pŽ¡(Á’hÞû™ŸW:NÓ <³á6777EüŽéˆS¨i¿³Ëÿ/ 7s¶Ÿl·Ûí Üç~Q©T*8Ch2™LNNNNú}N"ÀÉ0´ÿd©é…œñŒ×Aæ$9l¥Ùl6Yçݾ¾¾>ÒBP«Õj<…ÐKE۰ѼÿNÛ/麮ÿò—¿ü%Ïo€kг£ÕCˆÅb±¾¾¾>¿ÎKÈHĺa±‹t mHëõz}uuuÕî3’ V”ëȃ±±±1ëßIáw¸Å× M àÆ…¬$X½Ô^@Û!r¹\Ž%äñ BƵ®IöÕëõúÞÞÞé÷†a¯_¿~ûœæ¡ìDü”=€÷ܾ}ûv<ã>/•J%³ò`õ [ ƒñçýçý‹sÅ.ÿßéïÜBs`ÊÔuÀ®.bmmm-Œ¹î™L&C2 öwïÞ½{gݳ'‰Ä»wïÞ‘¢fPt"ÎÛ‡‡ÝÝÝݼõˆÎÎÎÎXá7h¹=N g …BÿÅÅÅ…Ýç4QTÛ/™{VMòš hy™RAš <Æaƒƒƒƒ´h»HÚû²±±±A2Z‘±¢ØßWÒ&õââââýû÷ïIç¤(tÀú<ÝওDµZ­ÚÍ!7)nÞ¼y³§§§ÇÉoÝB3$)ÊõÆWðg2™ŒUIBƵ®@}ÎEÎ!'È ãEĽe­êíÅæ?Ìø){ïyóæÍ›ãããcëß‘cgh=8880ïñHJ ɘKr@¢ëX²CÛ3Ñö‚~B*æbÐÈ©G2Äb±˜uŸ×ÓÓÓCš›ýýýý¤èD?h6›ÍO>ùä'sˆæ€AÜ }Á †aŸ~úé§vŸ‘NTè#L ð»‘us„6…¸änò[x¡yMX@Š?© ò › ¸$BTÂf³Ù\ZZZ")ÅÖQšÇ¸Ñh4Hãâk„5_UU•mÁº >===mµZ-Üç}}}}~(A<$ÌÆ#\¡Ðd2™¼¼¼¼tbL¢y(¼„¶yp³A*•J%¿.’Œ—eL$Sp-4Ñ2Ëc/ä(ïöÜpO¤Ž,÷V$c5‚ä1B×Jú½ ­t;Eö×4›Íæ½{÷î!…û“˜÷ïß¿7Q‰DWðŒ4_pÞE!;.H¿ ,é ´½ ßRAÃ^ šÅ@ƒ¦ø‡i} 9`¾þúë¯Åc ’P‘)tÆŠÅb·)´‚6‰^‡SÓ¼&$…1•J¥Þ½{÷޵Âp:N±1¢)ãŠòãJÑ41‹7¾Ùl6IÕhãñxüöíÛ·Ñÿic²z·¬ ¾•žžžž›7oÞd9–[X:I„e9%7ì$@ 6N9µ’L&“ïÞ½{çÅFHFï¿î-Ú¨bÙÓ™ ¨«‹­2»Ý>dzzzÚ@r@ E¤yÖ Þ§µ ‚„” ö‚€'†Ôa.h¿(h)1fãÕàEÅwšWóåË—/e ñš\.—cÝD™¡åm…¦iÚÓ§OŸòVãñ ‹¢Ùl6_¾|ù’ô³bLó‹I ª¾‚Õð€ƒTH’Gè’ €~Í!ZT„WÐæ‘ßPAtõðzL§=ÎQÐ?ýÓ?ýSQçÒi2ÞÏ{KJ]bZ"y"£Ö\6Ùȃ]- Rª.ÉPDšga70Ñ"AEÿSUUýÝï~÷;žuòË/¿ü’”ÏO+¸²²²âä\9PUUÝÝÝÝ%§Ìïß š…Ý‹IAójFÅê‹ÅbÿðÿðNÿäÉ“'²…í¸é©žN§Óó7ó7¢Ï‰ÄóçÏŸ“ÒÌŠ1-¯†ÕO›ß<=wY‹XÑÞ9†ž"‹¤3·sȉ²å'´Î¸VGN BÆ1¦7=ΉDâ¿øÅ/DœG"‘H|þùçŸ;ý½Œ2ÞÏ{K2Ò±T°&y"£ØÜoÙ„ «a·ç¡yñqó¬¼ÿ´úe""˜5MÓŽŽŽŽFFFFX¾"²X*ú“êPÙE„ –Ôc+vijae{{{›ÖYÏ\¤ü†µˆù|>/2÷œd´.Ê´¼ ™[ŒÑø§¿÷²EM±´óvg³Ù,.W›•Ÿüä'?qó{^h^`³bÌšWCƒµB' ¬^0?óÝ Ã0=zôˆV_ÁÎà b±"¢Ö…°vd%Ęžç^C*6Ä‚,mˆ~ß[Ã0ŒÜç¤âe4oÈ«W¯^E±Ð0 ѲÖý.RæÅÇíÃîýWz M·Ì©T*õöíÛ·¬Nd,`Ux;±  ÅßJØ ÕjµJÚ;7ÆÔÔÔ”ynÞ`Ý›{{» Kd©Pˆ èÑ(*$ÇÒóQvr¹\ÎRÍRQ׋â¬m‚ÌÐÚ:"P¾ ºF§=„EAËW>¼Ì‹§¨Ü}–wNäu²ÔW°ŽÄ "؉qÍ AÈø ÆD°ô8Gü–C²Èx§qoIÅ«‰DâíÛ·o­÷‡¶)–¥G·ßø-{H ½›SxåE>Ÿ`Ëö IDATϳ—µVG˜Á¥áš÷¸ïмø¸ýc'xÿYŠÿ¹¹Æl6›Ýßßßg5ó $™ÊR£IXÿãï¡ý FC‹ò?666f5¾ÝPþMp¹\.{©tÿüç?ÿ9ï¤F=Ã2qÍ ¼jkÛB¡PÈår9Òo­•êEÀ‚m3´°(E¹îý=222bÞ`°¶äó ZQ>3¤Í“Wᤴó#UïE°*Ö"a¹¯ÖûÄbñT‹„VÿDQ¼i[„ŒbLãe­V«Y þx-‡d“ñNêÞÒ:¶˜HR5fEQ”ùùùy«÷ß\H ɘa5LÚa•]~”ìƒ9å÷>Њ¥†aôöööŠú»–†Nç/‹ÿñŠU~cRA@E ¦./ÕjµJRü‘!ú§?ýéO­kG»Ýnÿô§?ý)Iž[;Lɺ>8Uþå{©0 ¤tó†â“<±X,ö›ßüæ7NÃ&Ëår9lF€­­­-\("˳1WªwjÏD Á¶Svi¹…F£±°°°`÷Y³ÙlNMMM @‚5ÏÞL*•JµZ­–·‚·%mS6;;;KòºÉÙuo'‰ æaÆòòò2Ïo¼Æ‹(~Ëø Æ¤õ8§Í!ZkP§È"ãÝä½Õu]r»Fà 1À5Q©¿Ô‰àÚkÚTi5€ñ²ø_µZ­òÔ.â5Øö‚€¸Z´v~fñ§¸Ë)æFùWS€™™™7ÞÎ%CØ)/P6XB†HE;庖Àððð0m,;O‰VdW䄦$Ór¿h¹ža"•J¥ž={ö̬̺1LÑ6e¸¶a¼­ºœ:DÔªT*–Pì°„Œ÷{LZsÚ"åG:ÅOï%Aß[´Y›˜˜˜pbHÈår9–i&ªÕj•´CU³ ÅÕ ª Qð¢øÚ£ñÔ=â5XXXXÀUiŽ¥ ±®'ȸë¤nm!V/«^¯×I÷âÿ (¼ÁíF…Mé…S! –!Z>µßB{cccæB em-D«Èï¢Ãqy²NêööööX”(kŽ£›‚(~ô* ?ŒAz^‚ñ~ŽI qJ…QÆ[‘åÞ:mª(ጠx}; T'ËN °†—J¥R2™Liš¦±Ô~Œèâ´:%¥R©d § ìÒ¯hœ&² µcsssóøøøxbbbB„qW×u=Ç'&&&Že­Y¡iš¶¶¶¶†ûÜ®àŸ7¬( ‘ÊårÙê‘t‚µ f´ßŽŽŽ†! €%O–¥_½_…êjµZÍ>I«0Ïj ª/»_°ì³ÂòüÈ¹à” s¨P(œzÃD2Þ1i ëb)`ÉCØd¼2ÜÛjµZ¥åöÓ(—Ëe¢À š¦ivï‚UqT” ÜÈ9¤, #¢‹ÿÑŠ÷år¹œ¹n‹± “ÉdXÇ7æ‚€†awîܹ#ÚK¯ëº~çÎ;²v¬ Õ¢h·ÛíG=b9÷ Šâ>Ô‘ÏçónŒ8‹ËF=‘H$ÆÆÆÆœŽí<…ãdÈÍ«×ëõ™™™Þß±Vf©Èï~…¾»i±DÒ2BË—ãů9„,À^U…jŽ[ BÆ{=&͈Ä:‡x ƒÒ›ŒÇä½u&KÂì q¼° ‹ìÜAjoiíómfsss³Ñh4ÒétZTyT Õ?á)þG*Þ‡žf'¯±€ZAÀ0¥TGÚÞš'mÁÖ€@›áîîîn§Ê‡Ó<RA!tn´|BZÎbдZ­Öééé)Ëwi½ã½Vbí¬ÊZ8kˆ½Èwàòy­©Œcúq\Q‘ñ¬÷–äíä‰ "E F͈6Ùü)ZQ”âV%Ï+hÅÿXÒ.¤N/8¥ÕáE!SR_yRqI ¼¸6( ½Š¤¢üز ¡Ž×Àbû!~o¼ïß¿Ÿ¤Œ›Q¢ŠtÑ ^EÆõ+ôÏi' dc¥õB—ÑpÉ+ãe3ˆyïŽ%É+Ö®’â+»¼Iep )Z‘÷}À!s'¿YüQ( ¸º$vJ}¥R©àêã$‰ÄÉÉɉˆ6}´k…ˆÍÎDˆ@Qåààà€µÀ˜_íhdÏgâYlEzE#J1æÉÇK«:³ef¸bUFH÷Ã+O<íZýý#u’ërmÉê•ä‘ñAŒI›C¬ï§È9Ľø «ìÈB¬©°$£š¨4‚NATñ?+¼J½®ëúÐÐÐn,—Ëe7íÖßYBQ b³“fàVŒ&+4O7Qð—æ5–¹ëæ%_$““““¤öRŠòã8šáŠÅÛGË)óÊhE[̼ ýÑIBÖ9ä–vLÓÓÓÓÐ{—šrÚïÍÒÂ’•N‘ñ2Þ[€=цdHçéehÑ¡nöL¼J½aÆààà .%.ŸÏçyê˜!u– µcªªª‡‡‡‡¸úWWWWW¤ØÉd2yyyyIú=Kmkq3vqHÜÈf³Y·Ö#EáÏ_¥p“šH¯¦WíqpE]̰xªƒ ݵ9ô3VÓ4mmmmôkR—¢\[oÇÆÆÆHǤ]£W‹0i1Ý’ÏI'‰0Î!7Д97½wóù|Þ¼BÆ1&Í@Çêݤmüxé/Û½åM7‘=Ì?›ÍfIÃóóós;¥¼X,yç§ìÄAZ#Yd)ú†§¨f )ÅŠ"fÏÄ¢Ô›BTÌ®V«Õ쾟N§Ó89AâñãÇqJ+K;v ¼ÜP”ë‰vuuuå&—„¶°[s”h¹h, 7͛ʓE ^µ ¥½°4¯‰ŸÜì y‚ÚxãÐ4M;888 µª³+ðB›'‹‹‹‹¤yBk)ÛSÆB*•JÍÎÎÎâ>ç)¨åU'‰°Í!·*“#òù|žW&W«Õj>ŸÏÛËoï÷˜4¢Ð½›´wÅ ~ËxÚÚéĘĽ%ÉZžêý´}‚ÌÑs^à·ìä€ÇSÌ. ˆ,þG‚¦ÔÛ‘Éd2¬­ri¤R©Ôøøø¸ÝgPø¯óùQ @¹\._]]]ñzn²Ùl–&ø­‹ì›7oÞã¾Ï†6777GzIyvZŽc2™LNNNN²k;Mi”¡§:í^Óž'Íâ*ÂÃÒ§^Qì•qZNr2™L®¬¬¬Ø}Fª²ª(ô¼Ï>ûì3޾窪ªëëëë^ö±Eæ(XBqåZ&³lÄ5MÓÎÏÏÏimý”ñAŒI3,'‰ÄãÇÛ}Æò®8ÁoO2V³Dà¼å~ß[Òš‹Åb´]Ú>!j…Ï‚’=€;XŸœR ÞÿãEñ?¼J=©˜ +4™ …ÿä$•J¥Z­V‹'R ‡m ä¹¹ººº"å• Á_.—Ë´¬‹,K85n3¡(tooˆKgÅ6ì˜Å)v4¥QQäPàÜnwwwwY”rVÌsqyyyIËùGà Ÿ†aììììÐÆ¶¾7Åb±H{Oháÿ±X,¶¿¿¿ÏÚVUUõÅ‹/H×Üh4››››´cùlsÈk6666H׋(—ËåV«Õ²“H³µ~Èø Ƥ–Å>TRÓ4íèèèˆU>ðâ§Œ'!YêžàÂëý¾·´ñÒétš¶ÓŒHQ-|¤ì‰ ,9¿4¬ïNñd‰êÁ¥"mmmm÷ÿhÑX^É ^¥žTLÒZ…ÿ¢µ`:Nã„«àÇUˤ…¢árZŠÅbqŸäMà Ñaµ®Ú)–VxCçbgý‹Ò(:Û)¬›C«òêõÆÛ ´Â'4EUQ>|ohžL+<ÚXà6fŠrŽÉbð)Ç«“æ º®ë[[[[,ßE2© &᥌÷{L–ÖŠòCµe‘÷‘„ß2>ŸÏç­Ê1ËXŠòCx½ªªê_|ñú»ß÷–%í ŸÏçíö(Ú˭ѵS‘Eö|à"i5.p¡Þæý!´´3/÷L¼J=­˜ R ,(ü|é€ %añ¦Z7¢•)ëy:µ¦‰…ʲ^§¢È“»Åº9´ZÅeÛD´Ûíö£G‘<Ïæ‰'Ï·1»ºººb Ç”mñï”9ÄÃêêê*®P˜"\7f¥R©ðäTú‰Ÿ2Þj¬fÍÏFrÅ΀è÷½eÉW·Û#°?£úÜ)²'J†a,///[ÿN«3b—bÔh4SSSSAGÊ„Åÿhð*õ´b‚vÒ¢dr ɪ×@ªÕßßß{nõz½ÞÝÝÝMú=®M¶Wxn y†¼X„œ†4麮¿zõê•Ès!qqqqáÆà ÛFæ7 F£1444Ä2wDÎÛ ,®².þ"æÐ·ß~û­¨óñšf³Ù¼wïÞ½0oÄöCörÌ™™™§÷´Ýn·ÿã?þã?œŸÝtšŒWï-NáAÔCŸ;AöD‘J¥R±æ‹“ꌋŢÕ!Àâèˆ"~ÿ£”úÃÃÃC–ï#å”e&¥OËæ¼ÅS@½^¯ß»wïIÉh6›Í©©©)ÞµZ­æ¦ß¸›Í /F£ñÿøÿèô÷óóóó²m`œ>ËZ­VUÝ”—R©Tâé¡)jÞ±7ÆØØØ˜¬‹¿Û9ô‡?üá¢ÏÉK¼g‘ñAŒéæýœŸŸŸÿÏÿüÏÿtw–×È*ãÝ9ü¾·v [ÜîDãU›aa–=Q¦P(¬ï„]Ú]gGGÔ µlõ¢KŽf³Ùüä“O>áY[?ýôÓOiû:RÁY(ü-nT*•ŠÛj’vÔjµk8ƒ“;J¥R‰¥ç8 dIóK-—Ëe'c•J¥’ŒE: Ã0ÆÆÆÆx7‡õz½>333ãÑia)•J¥®®®.'›A§×Šh·Ûí<ðs!®Õj5CG†aòà§Œÿ—ù—!Ï÷[Dx½Ÿ÷Í'§)Bhɶvªªªöõõõá>ÇujÑ4Måý¯dຮëñx€0ÀZ¾ð=^ŸO'b kîééé¹yóæM^áò÷¸9F½^¯ïííí¹=—N#*÷u}u¼¾¾¾>»Í9Í‹«XÞ¤”«X,Ûßßßw;F­V«…¥»”׈^¼"_@•ËqÅq=Šeò%½#Š÷—ÿOóâãòÿÁû€¢°¥\Ñ"_aõ_\\\¼ÿþ½çD£T*• _Ò¢xoqùÿ4/>.ÿ¼ÿ ææææh)W@´ %/_¾|)ƒW¸T*• ­Ã¢zo›Ífsddd„÷wa 3ä €tÔjµZЊa½^¯ß»wïž FˆNî-Ô|A×u}~~~žôz½^ïîîîvSX¨R©Tº¾§T*•x~‹ÆïêêêõÇÀ½ð‡jµZ½ÂÐjµZ©T*ô9áõ€·›cÖ¾ðÅb±h÷½ÃÃÃCUUÕ ®!Êžâüüü\Ó4-ès•…l6›%ݯb±XôûœH²Þî] ³ Ér§l]]]]U«ÕjÐçt`/,Ê?<#{TUUíî•߯U’AžD0DÒB„w è\ÀN4MÓÎÏÏÏq›E;ÀpøcHF¿¢&HïHŠ+zf¸r!‚»W^ÏkÒså¢" ËÆD€ða§°Ú…­ãžÈ’ Â`F`ˆ(°aü áÂîî¿Ýó…¢öø)qyÿ ë š€ ‚Øma£x ƒŒ` ·‹,‚ÎöØÝcÑ÷ ÷ÎÁ¢ "ÈÏ~ö³Ÿ}´öX°Npª¸*нÑ!ª!Ò¿þõ¯-êXš¦i¢Ž×Ɉ¾WwîÜ¹Ó 3»–5—êÄúBê¶#ÚGS yjEê ³p ¬ï!†´Ño!à' ½Ýo 8ÈSEÞ©áÀú;’,°ži #­}A*Ƥš@¬÷Ì ®æÂI$OÑbœƒöáÖw€žÀkp›YÜ&ÁPlˆ47ïûÆ[cÂɦßÉúd•74å–d*ʼn¤°ó*ÿ´è>¯÷ òA‹q:‡Hȸ¾³Îù £|6dÖÀúÒv’¥fé÷B8šÁm4D)ÿ^]Ëq½¾w;¬¹Ï’#m½ŽN’Y@x )i"ä&ëfÞnÇ2Ís}uõ¡¢a}÷X”x’âä÷»KÚìòÊAV…ðêŠÍ‚€¶'•M®óL/2šÈét:íä÷‰D"qpppàvÑL¥R©ÙÙÙYëßÛívûÁƒt]×׋ë+‹Es?‘H$NNNN¨Pv««««õz½nþ[,‹=}úôéÄÄÄÄÓ§OŸÆb±˜ùóz½^Ÿ™™™ñõD)±nÂ"óƒ@UUu}}}Ýú>yÁÂÂÂB£Ñh˜ÿ–H$»»»»¿øÅ/~ñäÉ“'ÖßÔjµZ¥R©Ž{ÿþýûæó¯Õjµ®ï©Õj5ów“ÉdòòòòÒníùú믿f¹]×õ/£@UUõÅ‹/¬Öø °Ûh¸Uþeº>’ÉdòÅ‹/Àà/º®ëóóóó,ßÝÚÚÚr‰QÆê=÷šJ¥R±zåq,///†a¾£ªªÚ××ׇþßjµZ§§§§Ö1ûûûû­Ñ§ N1öÒ;^,‹v^g'Ê?Î#Z*•JÖcáäñÚÚÚZ=ÙnÀ`„®ëz<wY ½'ˆ‡>1¾ßë;MÖµÛíöÄÄÄ„õžtuuuñ¾[à%`pÉììì¬L•ëeSŽí6QSþÉd2¹²²²ôyD e¡^¯×WWWWý:'èTUUïÞ½{—ôs8½½½½½4%ÝŠ]*€;%Ô †a½½½½¥R©dþ{½^¯ßºuë–SƒäídÀyë(ÿŠbú_*•J…B¡`÷ýJ¥R±Þ¿D"‘xüøñcÒ8èÞÛÍëX•J¥"jžyA*•J}^a†1666Fz?ûúúúD8Dü^ßÍ‘BVFchhh @€Kb±Xlqqq1èó@Ø-ÆAa·Ñp›ó/Óõ9áîÝ»w! ÀHÊBXCÿÍ5[¬é[ét:rס½&$¬!ø¼àR<›þf³ÙaU …BÁ¬XŽŒŒŒ¸•8#€H'.ìÜ©òo·Æ×jµNùG …‚Õ¥Tš¹Ñh4úûûûxÖeÁ0 cgggDZüZß­‘BV^¿~ýZ°iÍ+a] pŒŽŽŽÊ¾†³ìÛ S¹Àtwwwߺuë–ùøñx<îTùòúp 2ïq†‡‡‡yÇÜAò¹™“~c®´ÍZ³%‹Åö÷÷÷Y+Ã7oÞ¼ÙÓÓÓƒûüââââýû÷ï½çéí꣔û]Hõ­[·n}÷Ýw߉8þŸüÉŸüÉààà õ^9’øî»ï¾³®ñ™L&Ãò[«¥«««ëÛo¿ýÖÉy„ ’¹^¯×íIó:6111aW@R&H1{zzznÞ¼ySÄ8²¬ïß|óÍ7~ŒÂÀµ"@…Ëh-Ñd/p†ÎßI5vÖm2X®Y[ɰœ«ù™ËÒ:ǯë+‹E§×ÌÒƪ€‘æ˜è~ö7…^ªbqÓfÍŠÌÛó"ÉQNºDñý ûþü†$»ü0ÐÒÚ‰:Òš%˾’š¬“mœ!ËÚêõ{ú£€z½^ïîîî³ÝddµL›i6›Í©©©)šw÷ã?þØùºGÓ4mttt”ö=ÖP?tÏX -yŸ×W( Nsu]×·¶¶¶Hß!YÿÀŽl6›….- ­Ý IDAT€[n(Êat¢ð‹ÆÏ\&§ŒÑ*Ê:)ˆ”Éd2¬©fh–Ò«+>/l×Gbsss3,yz€üh𦭭­­}QÀlù·¶-—Ëe^¹x‡è5†œGæDçÂyÅã#…ŠOOOO‡Í3$^<Ÿ(ô}#E¾ŠŠñK^‡u]ˆt H¹L2@k©Òh4››››NŽ]*•J,Þpó }rrrBSØ­¹É¤°®O'&eÀ æÉϱ¥3ÚEÉŒyñ¶*ýv˜åVTÂÛI¡ˆ4YŸL&“————*vi-e.pšËårþœm¸@†ÜâÍB2™L¾xñâEŒAãåC‚'ý&‹Åž={öLÆÍÀ†ˆy†6þ¸¹öæÍ›7ÇÇÇǸß'‰ÄÛ·oßÂ<ú?žO'"Ã}+‹EžTÆX,{úôéSÊ/yÍf³¬{Y×ß ªªª_|ñÅ<¿ùâ‹/¾}ƒeH¥R©·oß¾M&“I7Ö\MÓ´££££¿üË¿üËýýý};ËàíÛ·oÇãñ8îív»ýüùóçNƧ,Š,ž3§y}N ¥+Èv®€ÜÐæKKMs¥tÑé.aÉgˆ¬?ÖNÒob±Xl}}}]ö= ð!Åb±èÇÌÛÛÛÛAmÌÌxÙgšä¥"…µÛíöÒÒÒo¡MRÞ“¬¹E€÷8­%Ñl6›Ÿ|òÉ'Q,øªišöôéÓ§ ü‡ƒ ×˜Ÿüä'?qúÛ»wïÞ•ÉÛàÁíÛ¼F×u}~~~žå»ét:MÊïîd‚z>aG–ûÖÓÓÓãÇšy-Óºh âÄÚS1¨¼¹¹¹9’Uicccm¦›ÍfóÞ½{÷êõzÝú=TØW€Ã\°Wà¨^¯×ïÝ»wϼy§µ•;;;;½ÙÏf³Ùt:fùn.—ËuÙ`Í[ÆÄõÙARÄYr«¶¶¶¶x=¶ét:M²ÞšçŒªtîÞPE?ÇêDün'Ù Ï‹¥ "j‡kM—@rÌM¼_ìø¹ÆÐ°¶Hf9îÀÀÀÀððð°Û±ï }Õ[±›gÝÝÝÝv{>´b8PQ¶(õœâùt2ß7«L)³ý’×a_¤(È[˜'ŸÏçý ‡J¥R)RèJ½^¯ïííí™ÿF2¸ÁNùWUUíëëë#ýNtÕyV«b©T*uuuuáZó麮ÇãñxWWWW­V«áÆòûú¼ —Ëå …BÁË1‡ J„aFooo¯Ý"FƒEh¿~ýú5*2éçX$úúúú‚VÒ‹BY*•J´v¸…B¡ÐÕ­âŠæ:VúûûûI¹Ì¸ž™ÞÞÞ^óœ÷s¡a·î꺮?xðàé]ŽÅb1ÚÚÈA___ÉCY«Õjñx<Ž3à7›ÍæÈÈÈËû (×Î7ÅGåzß,ƒ¡Î)ñx<~ûöíÛ,ßõûùà Õ£’ Yî›´àÖY$³®¯~ÉëNX¤0( ¿RŸN§Ó^ ?UUÕõõõu'!Ý,E^xh4©©©)'žnÑ- ''''iÞîR©TâQx3™LÆ©€‘¹E#mƒêN*]G–|ðz½^Ÿ™™™ b,ÒÜ–-ŸLvh­DyeV¥R©D½ž‚WȲÆÖx]×õW¯^½"ýžTÃÚsúýïÿ{Öc!1N!åd#o,ë‹Åb±û÷ïßg9ŽŸÏ‡I¡¶Ö“Yî¢V«ÕhvD¥R©ðFø%¯;e]Æ (üJ½×Åiù‡´nÃ0Œ±±±1·F€F£Ñ“¥Ím#]«ÕjN¼Ý†aR¥µaóÚë#ŸÏçÁ@†%ÜáMÄXgggg¤0ŸÏçÃìò‹T*•Ç}Þh4››››¼Çr=/‘e¡m Cô •J¥b£¤È–F£ÑÒu]GÊ‹$k‹1EQ”÷ïß¿¿¸¸¸À}d>´Ýó¡A’K2£ö'÷MQ®ç÷ÂÂÂÏot]×çæææX¿ï—¼î”uÁw )Á…ãÙ)õ™L&ƒ[´½*X­V«¤pÑz½^_]]]¥Ç0 cgggÇî3kÞ"NØ“B‚oÞ¼y³§§§‡v¢`éK¾±±±!j<¿¯O(ÿê*¸¢=³³³³2n d@UUuwww—”.Êðæf,Z¿hE¹–™_}õÕWPØ-rgggGkÔñ{!ñòåË—$ãŽÌÑg€8x /ã åd////[e ‡¦­dl1¦(×û}’W\T›¨çC¢X,I: Ũyñã¾!œ®±ûûûû¬¿óK^wʺX@&“ÉàBIì”úB¡PÀ)ȼÅiÐ^t¯ ]F£Ñèïïï·†…뺮ߺuë–µvÀøøø¸,Š- ÚÜ}H>ŸÏó´½…¬›‚ ai-Ön·Û=z$Bùw3V³Ùl:Qv̨ P²@ó(‡eÁŽ2­10/¢í9£V|n÷˜8C$-|¼P(hi23C 3ÏçóyZ7#¿žš¦içççç´Vzæbà²ä}3Ón·ÛÏŸ?îåŠâŸ¼î”u!ÐRއR¯ëº>444„‚"rŸ‹Åb‘ô¢ózÇÆÆÆ¬ž?;K/¢Ùl6—–––Ì÷„”+E ± Í“&º"¿ß×缑*(…kA«d2™œœœœtwöKk±ùùùyÞŽ ^U©T*nScPåè(ö’¦m4ƒƒƒ?Ï Àã÷,©š¨³†Óö»nri)¥23S©T*´ýй›‘´ÿ÷ëù Î fp¹ÌÔjµšŸužXñë¾ÑÇ œ^çõFX•z”…(n:Д'^A«ÀgÙlÚ…ýúÝò ‡È¢"aUÿvR [QèyT0°«««+“ÉdHÇÊd2VCÍûi×N‹Öâ.AŒ… E÷ ó±˜‹«P(œV17“N§ÓNù ž—hiD~†nvúû%‚(¯1@0†a¼~ýú5ë÷­ +k„Îs‹Åb,]/ Ã0–———qŸËR\Ì )â—¿žJ¥R‰¶_ ™ï<…^À®P©.Àèèè(ï¤%UfU”kåÿÁƒx½‚VÅ=ˆÀ†a¬ ”º0Ôבé¾r!• Z­Vig3Åb±¸¿¿¿ï¦úµ—Ê¿¢|(‰DblllŒô»J±nÚJôõõõuòF_¶ëÓu]ßÚÚÚÂ}.ºX­Pœ¬¹~‘J¥ROžäkmmm ’m×/œVA“fuà7a¼¾çÏŸ?ÇY\½PÈÃè5vï“•z½^Ÿ™™™ ÓXf슩^\\\°Ì‡ û0ÈJÉëîîîv«0áZñf2™ ­¦ ¿F†€T*•zûöí[\A:Rqé I¥R©ÙÙÙYÒwÚív{bbbÂZ‹Ä.EÎËçc6¬#Xê;¥Óé´ìžq?æ5.7¤R©Ô»wïÞá*eçr¹œ9TµäÀ X rø¡ü+ÊuøÍÎÎÎŽùoH°[NÀ¿zõêéé˜a]G¼¾o€¼f¨V«UœÞh4CCCCfœ”ïog,ÀAËÑ©ü“ ¼ònXZ}LOOO‹¨O 5÷"¤ØÏëÅíÛ·oÇãñ¸_ãÑzgGííím\T‘¢ˆ}ÏýË vmEƒFÓ4§Þ‹ÚƲÕjµNOOO…^HTÆ¢(ļ V¼´,4#«®ëz<ózbQ%zY=ÿŠBoÁj—Þ+ QÏÇ ©i§¬#^Ü7@N|7Ðòýëõz}pppÐì)#åûÛ p¤R©Ô³gÏž‘¥ùùùyuQEOÌäóù<Î@Ë“O$‰¹¹¹9§ã"åšæ÷Ê*è×õ‰ÂO…\UU•ÔC¸Ýn·i:‰jµZÅÉ„¨÷Üϱ: ’<£aW(•iãDrÔ#3~E=À8üT*• Myg9ä‰eõÀæóù¼ìyç$G­¶•(D=E!'ýTˆÃ4¯y ¼€™Z­VaµÚ p°(ÿ¹\.çupdµÅ½XµZ­F²¾á*ŽnnnnÒ •8Y0¬Æš7>‹ÅHʨSüº>*õ"D†ä¯¬¬¬<Ðfoh§S,‹4…\T¾¤ce³Ù¬Û Ã4/Läóùüááá!¯G÷þýû÷I²üââââýû÷ïÑÿIý·E¾¢(Ô$´´¨V·0Ž7ˆ\Ï3™L†µPÛ“'Ož„µ›ŸûQχfœE'Îk@>¤1°ïCð üTþI¡©æ\-kÞ Ý»Êވׯ_¿¶n˜YÂäå:•€EI&[$yÓåºfÏ),دë«V«U' ‚5yäS©TªÑh4œ.â$c‚µ+FØÉf³YÚ½(•J%ù’~Ž…úïÚµ…bannn7eˆI&“ÉËËËKV™ÁrïÏÎÎÎÌkM~8 RUUýÝï~÷;IKkL6ô©T*5>>>ãx7ަiÚÞÞÞž,ï1*ÔÆ†…bl²=¯éÔy ÈÅ ¤]8999!)4hG:É‹Å[Ä$—ËåXªªªëëëë´plkA>^Ðõ5›Íæ/ùË_š•wžft]×ûì3;ÃÇÆÆÆK1™r¹\nµZ-;¥ÍÒ3ßÛÛÛ£Y§yÃ{«Õj•6Ïüº>ë|fU¼ªÕjõòòò’äWE9>>>~óæÍôÔ÷ÇrߪÕjõêêꊶ™m·Ûí ÚñnßÖó=­°§¢\ E)ÿ~eUƒ¾ºººÂÍo+´ë\$áõÜÈçóù«««+œ¥±¤VÙ)4ùÁA„:ØŒŒŒŒ°þ&¬ï—ßµÆxÍ“6>>>Ž{ŸiUÔa÷ã(Êë>¯¡•–‚ç†J¥R¡urI&“ÉÉÉÉIóß²Ùl–ô^Ód(’Å2ÉŸS¢>¯k‚~OàUŒ*ÒA ëº>444T¯×ë¬5 HÇ9>>>¦Óu]ßÚÚÚb9®UéD°lšÍf“E©D“·©6Ÿh¡ÔŠâßõY1+^$X®AQè…oh/5ÏX´’ÂÇÒ¾ƒ<éN0/ž~Ž…Ã:¿­ÅFÑø´y²±±±áG[Bpe㚢\×…9888°þE~”Ëå2-å-Ú¢ ¼Ôã´Â†±X,öìÙ³gæM?­^Œ#n3<†Öl6›¥É%·¹î†acccc$#ËšÔ)Èö|œó? jàF£Ñc Mæý¾ †aðx‡HǹsçΖﮮ®®Þ½{÷.ËFÙ •J¥òðáÇ,¨ü="Æõëú¼BdÛF£±°°°àÇX@ø¨×ëõ½½½½ ÏC4¤”ùŒ'Ö¿‹”c Ö/@8Hóõ`ÿÇñòØÑ´ÛíöÒÒÒ’_‹ôòòò2¼3€F£1555ÕiFšÑ«Ùl6§¦¦¦hE9bñ‚f³Ù|ùòåK§¿ÿöÛo¿…q¼ÇK¼lu¸'ˆçóß Íf³ùÛßþö·<¿ùíoû[˜hìø¥$¥Œ‡Õàg;8QE-Î#ŒÑT¬°½XBj9«¬·cuuuÕÉu4Æ¿þë¿þ+Œãí8^€«£išöÇ?þñ<Å£æï^Ôêa¥ç5.¤éˆU‘¥õñ5kïZÑãz}}"h4þþþ~?r?Ǽ§R©TX[B±P*•J<ÑW^á…Îcô2 ÃìÅ²Ó jM³Ùl.---±²5ÃÉã8G4µZ­F*VÍZü—%÷ÛÚõ C{>^Ó©ó`èp …BÁÁê-Ìd2ZµZNÚ¦‰¾¾L&“Áµ_ä­$)\º®ëñx<îvsË2NdnÊ3_Q¨«««K&«½aFooo/ïõX©×ëõîîîn^£º¯näV§¦RÈHPkŒHPQ_–kpSôÆáÉ"4>¯’„+þËRü”Ö63ìÈð|¼ óI@Öd·UÈi¤R©T«ÕjÑ*žó´ÝbÁ‹ëc9&‚µ=›ˆñDŒtÖ÷VÉ^fh­q®®ð­ýûêJ¼Üø jéüEž3ŒãVyÀ;6KûmV¼’…" Ý?ûL¯žÏÿoïÞ}›ÈÚŽ_½¥­¦ˆ“æ#–Š‹“n•ÅlaK)@+E¦X6D²‘6HØR(@Bl”†ÉŽRG)Ò&N B(q \Dvϯ`ç·³³çœ™ñe.ö÷So9¾ç<çyž#cunÖÏöˆ£ð¹ߨT*fu£ð‹B¡PàØÀÌx^ÑK ~P¿/˜¡F~\ÈXøÝEP€‘öðáÇÆôª'Ož<é7ê ÆÓÆÆÆ†^&‡ÃOŸ>}êÅdÖÍÆÂNÅãñøÊÊÊŠ×ã¤v»Ýn6›MÙõóóóóœ_"(`¤5Ææææ¦þÿX,»ÿþ}/Ç‚©V«Õööööôÿ'‰ÄúúúºÝû÷Û„´×¾'n‰ÇãñýýýýX,“Ý&¨ÛÈ}úôé“ìºX,;>>>ösV #€‘÷áÇÆÿ¯¬¬¬¥½0Od˜›»ÚiÔ–Íf³¡P(4777ç‡É³¬nýäääD5ù×´Ÿ—Åööö¶ªI´yw#ÊÈà'0ò®\¹rÅøÿH$™™™™ñj< ¸.]ºtÉøÿÉÉÉɉ‰‰‰^/ŸÏçCüºâïT½^¯ommmy=Ž^}þüù³×ãúE§7ÔEW3™L&—Ëå¼}u[t^Q( ©T*埂®Ûív=zôÈ ½h·ÛíG=ÄVÑ€—`d”J¥’9åªT*•Ì·ët:ÓÓÓS/Æ‚At^!ZT8;;;;???÷bŒAÑív»ËËËË~m\hW­V«]½zõª±Á440v6777FÃëqƒR«Õj‘H$¢§Š¦Óé´×c€qÔ¦vn(‹ÅP(ŠD"‘ OþuF£1555 …BÙl6ëõx`ì8ÙS7ˆ{Ï÷Èܱÿ;>aõcýõëׯtþvd2™ üÀªŽzTXÕy …‚“dzӔ®Óét’ÉdrPk£ì9Q ø•J¥"›¸f2™Œ×ã$UУ—Ér4ª&ÿ½>>ÀÀÉ‚½¬\û•jBÝkwWUð¤ß¿C0l²À¸Ól80…B¡0Ê“HÙd}Г½tBözÚ-­ –a•¯€éwÒêWƒ~^²Ç3—Lô³²B0 V=€¬~ÛÀ‘M&ƒš8èôFÑë£Z%‘Mä­N¨ÍIï;¿q`ˆ&ÍA<M¢{ü‹^»%¢’U€0xViϽ–A!Ê`3þVÈAÏE£Ñ诿þú«×ãpâ§Ÿ~úi'óƒ~¼ŸþùçA=V¿d#ó„ÈÏÁÕä.ˆ,+¢÷ÂîIºhÔ,'`œ‰&÷¢c·ÝÛyMVާë·ta\·KcÀjUHÓä+C~™\[íÊà—Éyœª“Hókn~Oz™È‹î3.'²ÔÌŸ ;“>Ñk:¬`™Qýqò^™_?¿çÍT“ô~ÆlU*A_À¬u|W@ÀnŸ»Í½ ;Ù7ór•»— óI¨y‚*;Iµ{RÿýûxMˆ¨‰>Vßï^îÓ+¿ìtÖ·âöødï•è¤Z¿m/}¾ÿç÷Kö{ÒËwP5Æ6¿æ×Ìé–Àß¿»{ܵڵöØùÜ«^ÓQë±åƒ Œj#tà?^þôìÙ³g±X,f¼¬Z­VËårYÿ<¯¬¬¬˜ïk¾’ÉdòùóçÏíÜvuuuÕéŠÕÉÝoÑë¬ ‡Ãáóc]»víÚÅ‹/ʳÝn·=zô¨Ûív—çr¹œhW ÑëôÇüÑh4vžÃ0¨~¼ýrbd7Àä‡`X?šÍfÓüYzòäÉÙD%ÇŸôbLã(F×ÖÖÖD×}øðáƒÛã0FìžÄöÊí“7·žÏ¨ý#ÑäKt’+šËN8UQrÕ ´jõÌéßêt:{÷îݳÓ×@ÄÎdÀÉã:ÙvËø8æ×]öטU ûû^F¶|ÞulqšÐËç@ô8A"ú®‹2TdŸ)Õs·ûzÊÞïAgô³b/ã 3œ|þì|æÕ`ª÷¨×Ô}ÕqÖnp®—Çðk€ê}òKp5Húɇ’$»YŽªÛÙ=>Û9¿Ñ‘€qGþemmm͸ʣiÿ^N&“Éëׯ_7Þ¦Ûív=zô¨Ýn·]ªÐúúúz"‘H/ëv»ÝååååW¯^½ºsçÎóªÕŠP¡P(är¹œÓ±$‰Ä—/_¾ˆN\—–––Œ¯sµZ­†þR­V«æÇùöíÛ·ïß¿7¯ÜÈV D«iáp8üæÍ›77nܸñæÍ›7æ÷¹^¯×ïÞ½{×éó„B¡P(•J%»·Ïår¹ MªUŸ¿ÛØØØ0gô ãIÑÎÎÎŽù3¥:.T*•ŠÝïU.—Ëùý¤+hŸIMû‘1´»»»k¼,‹Åö÷÷÷“ׯ_¿6W)´µµµ%ʇÃaÙj©Š*àùóçÏ­¾ñx<.:VjZðVþ5Müû¨i?Vÿ_¼xñ‹1#ÕêÿÙÙÙÙùùù¹Ûc0>àD{sJ4>}úô©ù„èåË—/kµZÍ¥¡ %“Éäêêêªùò<ÐÇ&;!L¥R©a¤k‡ÃáðÓ§OŸš'—.]º¤ÿ»Ûív7666ôÿ§Óét6›ÍZ=v½^¯ommm‰®“•Äb±Øû÷ïß›K¼ àd2™L¯§©¼^’}ü®V«Õ^¾|ù²—ûÊŽ ª”jZ vŽ[IDAT™T*•ò{ —tu¯‰‚…±X,vrrr¢wÌï•ù˜5(ív»-{Ü^Ѳc¾ï£¬L+ˆ“Ù '˜¹›7oÞb4MÓvwww½^H¬ðc\À?˜WÿE5¢®jµZÍçóy·Æ)" L‹Å¢ùD­V«ÕW²÷yPDÙ}š6¼`ÄT«ÿšFý?Р·ç3jGÓÔžu¢ú+;õjnôæöwÆÛY=_Yí§q¼V[ùYÓiš±Ÿ·C”Í\óÛK?¯‰¾S~Ý:͉~·Y½çN:Ïûéý¶êµáå8œ¾NvúôòÜT¿c²1ªzô’}vÍ¿²ÛÙýÛªc®µöªï,«½ë¥€êû0Jõÿšæ]»¬2ÈÀ¨"š¦‰WÏEÝüÍéÝn·{çÎ;^§ŠRÈíd%äóù¼yå,•J¥D'hÙl6kg•&N§E«q““““šöïÕ2«ÇÌçóyãJÑÜÜÜœ“Á‡>ì'“`Xâñx|~~~^4¦åååeã c£Ñh\¾|ù²hÕ5H«ê¢l ©ÕjµH$­bF"‘ˆ*“Dôž«v•øéýÞÞÞÞv²rîgz¶Â7nˆžS6›ÍZ½¿ƒ¢z]{ÍÐ4y&€q—”d2™|÷îÝ;ójyÓþu²ÕMÓ´·oß¾u{<ãÊjõŸúnø¯×€?èR«Û¥Óé´ãqªü—^î›ÿ‹Õmœ<æööööêêêªñ„+‰Dffff¼–è'önÿ]+3333‘H$b¾\VŽ ×››†ÃáðÒÒÒ’×=(Œ2™LFÔÔpsssÓ뀙—ÌAD«ôã/^¼XYYYÑï‡ÃÆ/™·ÆÓ4Mûüùó磣£#¯ÆÔ/=ÀãõööööD)øzIU¯Ç|«çç‡ç?H¢Þ>:UX›˜˜˜˜œœœ”]o>¾©jÿ5úîøO¹\.«j­j÷T5’¡P(äv]¸[ÏgÔþ쳓&+ê†î§I‹_˜wBд'LÛÛÛÛ²ûȺ„›*ºI–Î)šü‹Å"ß¹²“q~~~~vvvf¼L¶W½›Dý+ü²Ê(P­L÷“0nDÇYÎþ˜šf{{{{z`Újõ_Ó¨ÿàJ›ô‰ÞÉÉɉêý±š¶Ûív³Ùlš/Ÿžžžöë„@/aò?:âñx|ß|,ðÃn(£BÕ ÐOe ~Çã++++¢ë¬‚­°¦ D:]ýoµZ­ýýýýAq”¨šªºUžŒß†Õ„è‡Ó=êahÕ~k K¥RÉ‹ ‚@/QÝF”fëåJ™lòO†Ç`5ÆÁÁÁè:½ìÇí1j…:è¥*^‹F£ÑÅÅÅEÙõÆÕMóGÖhšOÆ.ªvVWÃápxgggGpbaêuúq!ëví—.¹v‚Š*ƒê8_*•J£Öá¹æU];¹û÷ïß7÷ e¸!™L&™ü»CU°¸¸¸è׬¿¸}ûömÙuÍf³IúïTõÿl­ÀÏ< …·oß¾©Ò¢¬”J¥’Ûê`<ÈN Z­VkvvvV”Š&ë¢=Jô‰¿¨I—¦ýØMÁjýéÓ§OæËŒ»%¬O‡¨?G,‹íïïïs@TÏ¿ººº*ËêJ&“ÉÕÕÕUãe^­\f2™Œ¨¿“ÿái6›MÙ±”25Ù.+:?wÿ·¸õ: ªÊ®0¯þ»%¯ïyÐWýYY…ŸÉN ªÕjUµuŸ¨3ø¨ÐO0d³D"‘øòåË»e;V)áÑh4*j¦è·Õ¬r¹\ž5×0Çb±Øýû÷ï{5.¯é;9/Ó³ºÌ\Ù„{cccÃí÷ZV”Íf³Lþ‡çèèèèóçÏŸE×r@4þùçŸö“á š ú©ÞüÕ«W¯‚8!•¥ô³úÀï< D£Ñèû÷ïß÷³ê¸A¶MÕʉ*í2ÈdµÏVÂápøÝ»wïÌAQ·ÕI½¬‘’(›ÀkF£±¹¹¹i¾|ÜS—Ëår¹Z­VÍ—çr¹œq¥J4á®V«U7÷b—«»Ýn÷Æ7‚º/|PÈš~êFù»4777÷ñãǽNŽU¿C~èµ¢·nÞ¼yÓËqôBUÿïÕêÿ¸H&“ÉN§Ó1þVÐ pÆ“Àëׯ_3ùG©šùd2™ŒÝÕñ yöìÙ³^w@‡Ãá§OŸ>5ž¬Ëº|ËRÂeÛ(ÑÍ:xÒétZP©×ëõ»wïÞÒþ%™L&¿|ùòÅü{U¯×ëÿûßÿþÇI¾;TÁ½ • 9‹ÅbÇÇÇÇN'7²L)×Sñx<þñãÇA=”Õÿ³ú?|¢ K¶Yœq=àdr¤o5ŽõÕ£¦Ýn·çæææDï§*•^µåËÜÜÜܰO`NOOO;NÇ|y.—˙ӕõÕŒQÝ-`D"‘0®öȺ|‹2T'Œ~ífmÕ%zÜ¥Óét±X,Ú¹m±X,ºñ× …‚¬ÞßÍq@½ãƒ]$‚N/‘qÒðXÕ NӼ͘’5Ò Yy«ÿ½Që©ÎØe°ÏÕ€lõάX,C¡PH–ZY«Õj‘H$ …BNW'D Ëtætå~›YúÝ ÊÖÖÖÖŒY¢€žy·Õ!ªšpÕ‰E( ‰êôe2™LÆn¥L&“‘}&vwww™@þÏçó¡P($ è¿nÕÙ[¥üSïï>U#ÀqZùsÒðXV¾¦ójMY_ ‘5M§ÓiÙ}ôcŒh¢ë·¦~~} ®dµ»FN»)§Óé´“xÀ QòqdÕMÚ sÊn­V«=xðàA¯çvM¸¦ýHË=999QÉ2A(W“»=á–•¨™R*ìG- Ñß5ÔM$‰oß¾}3ßvP[[ºE–‰¥§ A.—ËÙÙbUÕˆÖ«m4e4'.]ºtÉÉåþÍÕ€Õ bµZ­ör²×h4vÓH§¶¶¶¶êõzÝîí[­Vë÷ßÿ21QÊn¹\.÷òv»&|U&–¦æ‰¿ªùa*•JY­²ª‚"N§szzz:ˆqÚU©T*ìú#/>‡~p-`µ‚Hãø•Þ¿ÀN¹I«Õj-,,,Ø©W «tÒAÈçóùl6›µ{û Õb·Z­Öììì,]ãÁšžžžRFƒ]ª½6t›^R#ëcµ¥.‚Í*x甪¹å¨€ap-`5ðk/@—N§Ó.\¸ ÊÐëƒõcŸ £ Nþ¬RpE¯«T5út»&\4>Ùç@D¯ì$×NM¥ãjà ðšÕV€£,ŸÏçeÇCUs@?dEÈvÑЋŢªf£mÐ[QŽúŽ À ý×­?¤ªGÓ4ï·¤ìг¼‡Ûô(þ º6ÛIûÓ(ýþ­a×ÏÁ8ðû„$ÿ¯Ç÷ÔjµÚÕ«W¯îïïÁ¥R©tåÊ•+v?ƒžx‰d2™ŒªÊòòò²ªJoÞ:¼Ž&¿½nzðNêå¼ßjw ö¸–`Õ¤çíÛ·oÝ g¹wppp@º'Ø×h4—/_¾,Ë<²ÛÐ ªf­V«uõêÕ«ôAnm9ÙË– ²æ³~ýrµ €à’m×çÁ>pNÏ<’õðK ´¬ü€z¹ðo Éd2Ùét:ýÔTëDµšAQ«Õj{{{{ý`ppD?¨V«U;·§î@?¬:ûmµ{âñxüãÇín©§Z]í¥Yšår¹,Ûº0‹ÅŽ“ÉdrXþ2jÛ"£Àµ€UªÕ.ü%N§C¡PHÖªZ­Vé¦kÍΊŠc€eÐxmfffFµ-訧'“Éäñññ±h Àn·Û½qãÆ ó€ªU×p8vI…¾u¡([,‡wvvv …Ba˜cˆ¹°JZ\\\ôC÷ZÀL½îÅ(F¼ûÙF‡×€•éééép8]×ív»ƒÚ¢Ô …BagggGôüU[êžžžv:ŽìqÝXt ÒÖ…O…B¡ j*Ìç£Îµ€ÕÑÅ‹/^»víš[ãþ§š¬v:Îééé©›ãqK¥R©ÈêýëõzýòåË—e™:ççççgggg²Ç–mÕ7húÖ…²’1Y_€x<ÿúõëWÕÎ/²~•J¥¢ºßáááá¨.89}Ýúy—k€F£Ñ8888]‡ÃkkkknøŸj²zvvvv~~~îæxÜ Úù Z­VçæææT ÿÚív[•1===íæ$8N§eåbw¹ÚPÖV—J¥R½Ô„Åãñ¸,J‚Éj ÀÝÝÝ]?t¾w‹¹ÙŸŠêœkrrrrbbbbp#³–ÏçóÙl6ëæß„·TN§C3HÀÿÉd2UêÕ^³¹\.§º¿qB¿µµµ%«3>ž“ @¥R©œœœœèÍqÜz>£öwð›k×®]»xñâEÑuÝn·»½½½íö˜¼’Íf³æf*ûûûû²-[c±Xlaaaap£³GµCÀ®f´ÛíöÆÆÆ†ÕíôIk&“Ɉ®7FS©Tjð#^[ZZZ’5üüùó磣£#·Çä6½Ó¿ÓF§V¥—·oß¾ÝÿèœSí>Wšö#úkwÿðR©T­ZWüÀhRÕÿCú¿ªÓ¿ª2·ûYíך¦iwïÞ½ËAÈÄãñøüüü¼èºqHÿ?<<?~,Ú‚9‹Åîß¿ßîã|úôé“ñÿ‹‹‹‹ƒÊP•e‰:ÿë––––D÷S}vD'ós€QCÀȱÓL´T*•¬gÔ÷ü$F×ÖÖÖD×½|ùò%«²ýk·ÛmÙdØÉ–€æZûÉÉÉɉ‰‰‰AŒQ¶ý£jõ_4&jEbbbbrrrÒÎãÀ¨ ì5¸ÇœŠ­«×ëõÇ?öbL£¨\.—üt±X,öìÙ³gšöwUÔs¡P(R©TjcS5T­þÇãñøüüü¼ùò^2GR©TJÔg R©Tè àCv·Utc+ §ÇÐA/eü¬$ÊÆkÕ¬ÍióJ£…&€Wÿõzà7­V«uëÖ­[¬þ€µÝÝÝÝA/FcjjjÊéýô-¤Þ¯Ýn·wwwwE™'0ª(ƒjµZššš’¥šþV­V«ù|>ïõ8z•Ïçó¢’Œ¾|>Ÿ ¤Óé´×côR@j'ü-“ÉdTÇÌQ+“²*°*CÀè¡€1@£Š0Fþ¸ø¢ ÇÝIEND®B`‚CubicSDR-0.2.3/font/vera_sans_mono72.bmfc000066400000000000000000000020121322677621400201000ustar00rootroot00000000000000# AngelCode Bitmap Font Generator configuration file fileVersion=1 # font settings fontName=Bitstream Vera Sans Mono fontFile=VeraMoBd.ttf charSet=0 fontSize=72 aa=1 scaleH=100 useSmoothing=1 isBold=0 isItalic=0 useUnicode=1 disableBoxChars=1 outputInvalidCharGlyph=0 dontIncludeKerningPairs=0 useHinting=1 renderFromOutline=0 useClearType=1 # character alignment paddingDown=0 paddingUp=0 paddingRight=0 paddingLeft=0 spacingHoriz=1 spacingVert=1 useFixedHeight=1 forceZero=0 # output file outWidth=1024 outHeight=1024 outBitDepth=32 fontDescFormat=0 fourChnlPacked=0 textureFormat=png textureCompression=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 invA=0 invR=0 invG=0 invB=0 # outline outlineThickness=0 # selected chars chars=32-126,160-255,262-263,268-269,273,286-287,304-305,321-322,338-339,350-353,376,381-382,402,710-711 chars=728-733,937,960,8211-8212,8216-8218,8220-8222,8224-8226,8230,8240,8249-8250,8364,8482,8706,8710 chars=8719,8721-8722,8725,8729-8730,8734,8747,8776,8800,8804-8805,9674 # imported icon images CubicSDR-0.2.3/font/vera_sans_mono72.fnt000066400000000000000000000705341322677621400177760ustar00rootroot00000000000000info face="Bitstream Vera Sans Mono" size=72 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 common lineHeight=72 base=57 scaleW=1024 scaleH=1024 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 page id=0 file="vera_sans_mono72_0.png" chars count=254 char id=32 x=1020 y=0 width=3 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=33 x=609 y=511 width=9 height=72 xoffset=14 yoffset=0 xadvance=37 page=0 chnl=15 char id=34 x=922 y=438 width=25 height=72 xoffset=6 yoffset=0 xadvance=37 page=0 chnl=15 char id=35 x=80 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=36 x=689 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=37 x=479 y=0 width=38 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=38 x=518 y=0 width=38 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=39 x=599 y=511 width=9 height=72 xoffset=14 yoffset=0 xadvance=37 page=0 chnl=15 char id=40 x=334 y=511 width=18 height=72 xoffset=11 yoffset=0 xadvance=37 page=0 chnl=15 char id=41 x=372 y=511 width=17 height=72 xoffset=9 yoffset=0 xadvance=37 page=0 chnl=15 char id=42 x=753 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=43 x=884 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=44 x=460 y=511 width=15 height=72 xoffset=10 yoffset=0 xadvance=37 page=0 chnl=15 char id=45 x=206 y=511 width=21 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=46 x=564 y=511 width=11 height=72 xoffset=13 yoffset=0 xadvance=37 page=0 chnl=15 char id=47 x=879 y=219 width=32 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=48 x=785 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=49 x=472 y=365 width=30 height=72 xoffset=5 yoffset=0 xadvance=37 page=0 chnl=15 char id=50 x=534 y=365 width=30 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=51 x=817 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=52 x=623 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=53 x=844 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=54 x=780 y=219 width=32 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=55 x=813 y=365 width=30 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=56 x=849 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=57 x=881 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=58 x=576 y=511 width=11 height=72 xoffset=13 yoffset=0 xadvance=37 page=0 chnl=15 char id=59 x=1009 y=292 width=14 height=72 xoffset=10 yoffset=0 xadvance=37 page=0 chnl=15 char id=60 x=691 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=61 x=759 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=62 x=793 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=63 x=866 y=438 width=27 height=72 xoffset=6 yoffset=0 xadvance=37 page=0 chnl=15 char id=64 x=906 y=0 width=37 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=65 x=266 y=73 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=66 x=0 y=219 width=33 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=67 x=720 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=68 x=462 y=292 width=32 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=69 x=658 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=70 x=210 y=438 width=29 height=72 xoffset=5 yoffset=0 xadvance=37 page=0 chnl=15 char id=71 x=945 y=219 width=32 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=72 x=945 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=73 x=180 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=74 x=995 y=365 width=28 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=75 x=560 y=73 width=35 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=76 x=596 y=365 width=30 height=72 xoffset=6 yoffset=0 xadvance=37 page=0 chnl=15 char id=77 x=306 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=78 x=977 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=79 x=340 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=80 x=0 y=365 width=31 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=81 x=408 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=82 x=596 y=73 width=35 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=83 x=32 y=365 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=84 x=442 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=85 x=476 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=86 x=452 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=87 x=0 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=88 x=76 y=73 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=89 x=557 y=0 width=38 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=90 x=510 y=219 width=33 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=91 x=390 y=511 width=17 height=72 xoffset=12 yoffset=0 xadvance=37 page=0 chnl=15 char id=92 x=396 y=292 width=32 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=93 x=408 y=511 width=17 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=94 x=776 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=95 x=240 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=96 x=314 y=511 width=19 height=72 xoffset=5 yoffset=0 xadvance=37 page=0 chnl=15 char id=97 x=0 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=98 x=64 y=365 width=31 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=99 x=935 y=365 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=100 x=912 y=219 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=101 x=35 y=146 width=34 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=102 x=379 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=103 x=33 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=104 x=905 y=365 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=105 x=264 y=292 width=32 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=106 x=72 y=511 width=22 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=107 x=453 y=146 width=33 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=108 x=429 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=109 x=956 y=73 width=34 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=110 x=630 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=111 x=487 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=112 x=96 y=365 width=31 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=113 x=747 y=219 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=114 x=837 y=438 width=28 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=115 x=600 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=116 x=286 y=365 width=30 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=117 x=450 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=118 x=140 y=146 width=34 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=119 x=200 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=120 x=632 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=121 x=668 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=122 x=317 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=123 x=750 y=438 width=28 height=72 xoffset=5 yoffset=0 xadvance=37 page=0 chnl=15 char id=124 x=639 y=511 width=9 height=72 xoffset=14 yoffset=0 xadvance=37 page=0 chnl=15 char id=125 x=808 y=438 width=28 height=72 xoffset=5 yoffset=0 xadvance=37 page=0 chnl=15 char id=126 x=657 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=160 x=649 y=511 width=3 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=161 x=619 y=511 width=9 height=72 xoffset=14 yoffset=0 xadvance=37 page=0 chnl=15 char id=162 x=779 y=438 width=28 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=163 x=363 y=292 width=32 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=164 x=360 y=438 width=29 height=72 xoffset=5 yoffset=0 xadvance=37 page=0 chnl=15 char id=165 x=674 y=0 width=38 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=166 x=629 y=511 width=9 height=72 xoffset=14 yoffset=0 xadvance=37 page=0 chnl=15 char id=167 x=150 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=168 x=250 y=511 width=21 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=169 x=160 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=170 x=48 y=511 width=23 height=72 xoffset=7 yoffset=0 xadvance=37 page=0 chnl=15 char id=171 x=0 y=438 width=29 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=172 x=34 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=173 x=184 y=511 width=21 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=174 x=400 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=175 x=140 y=511 width=21 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=176 x=118 y=511 width=21 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=177 x=204 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=178 x=272 y=511 width=21 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=179 x=95 y=511 width=22 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=180 x=353 y=511 width=18 height=72 xoffset=14 yoffset=0 xadvance=37 page=0 chnl=15 char id=181 x=589 y=146 width=33 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=182 x=495 y=292 width=32 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=183 x=1011 y=219 width=11 height=72 xoffset=13 yoffset=0 xadvance=37 page=0 chnl=15 char id=184 x=426 y=511 width=16 height=72 xoffset=10 yoffset=0 xadvance=37 page=0 chnl=15 char id=185 x=162 y=511 width=21 height=72 xoffset=9 yoffset=0 xadvance=37 page=0 chnl=15 char id=186 x=998 y=438 width=23 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=187 x=690 y=438 width=29 height=72 xoffset=5 yoffset=0 xadvance=37 page=0 chnl=15 char id=188 x=175 y=146 width=34 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=189 x=105 y=146 width=34 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=190 x=0 y=146 width=34 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=191 x=894 y=438 width=27 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=192 x=868 y=0 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=193 x=114 y=73 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=194 x=944 y=0 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=195 x=228 y=73 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=196 x=190 y=73 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=197 x=152 y=73 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=198 x=752 y=0 width=38 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=199 x=627 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=200 x=782 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=201 x=255 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=202 x=503 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=203 x=441 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=204 x=420 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=205 x=120 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=206 x=30 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=207 x=660 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=208 x=378 y=73 width=36 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=209 x=160 y=365 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=210 x=238 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=211 x=725 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=212 x=170 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=213 x=136 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=214 x=68 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=215 x=192 y=365 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=216 x=120 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=217 x=963 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=218 x=895 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=219 x=861 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=220 x=827 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=221 x=791 y=0 width=38 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=222 x=561 y=292 width=31 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=223 x=929 y=146 width=33 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=224 x=528 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=225 x=231 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=226 x=165 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=227 x=132 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=228 x=99 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=229 x=66 y=292 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=230 x=38 y=73 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=231 x=510 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=232 x=280 y=146 width=34 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=233 x=245 y=146 width=34 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=234 x=210 y=146 width=34 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=235 x=70 y=146 width=34 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=236 x=330 y=292 width=32 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=237 x=198 y=292 width=32 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=238 x=978 y=219 width=32 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=239 x=991 y=73 width=32 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=240 x=385 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=241 x=540 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=242 x=646 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=243 x=578 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=244 x=544 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=245 x=272 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=246 x=102 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=247 x=524 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=248 x=830 y=0 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=249 x=240 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=250 x=270 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=251 x=300 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=252 x=330 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=253 x=488 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=254 x=593 y=292 width=31 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=255 x=704 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=262 x=224 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=263 x=625 y=292 width=31 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=268 x=657 y=292 width=31 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=269 x=410 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=273 x=341 y=73 width=36 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=286 x=714 y=219 width=32 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=287 x=813 y=219 width=32 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=304 x=390 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=305 x=846 y=219 width=32 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=321 x=713 y=0 width=38 height=72 xoffset=-2 yoffset=0 xadvance=37 page=0 chnl=15 char id=322 x=315 y=146 width=34 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=338 x=0 y=73 width=37 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=339 x=982 y=0 width=37 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=350 x=689 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=351 x=570 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=352 x=721 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=353 x=480 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=376 x=596 y=0 width=38 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=381 x=555 y=146 width=33 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=382 x=751 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=402 x=304 y=73 width=36 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=710 x=948 y=438 width=24 height=72 xoffset=7 yoffset=0 xadvance=37 page=0 chnl=15 char id=711 x=973 y=438 width=24 height=72 xoffset=7 yoffset=0 xadvance=37 page=0 chnl=15 char id=728 x=228 y=511 width=21 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=729 x=588 y=511 width=10 height=72 xoffset=14 yoffset=0 xadvance=37 page=0 chnl=15 char id=730 x=294 y=511 width=19 height=72 xoffset=9 yoffset=0 xadvance=37 page=0 chnl=15 char id=731 x=492 y=511 width=14 height=72 xoffset=13 yoffset=0 xadvance=37 page=0 chnl=15 char id=732 x=24 y=511 width=23 height=72 xoffset=7 yoffset=0 xadvance=37 page=0 chnl=15 char id=733 x=997 y=146 width=25 height=72 xoffset=8 yoffset=0 xadvance=37 page=0 chnl=15 char id=937 x=521 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=960 x=440 y=0 width=38 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=8211 x=320 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8212 x=40 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8216 x=507 y=511 width=14 height=72 xoffset=12 yoffset=0 xadvance=37 page=0 chnl=15 char id=8217 x=537 y=511 width=14 height=72 xoffset=12 yoffset=0 xadvance=37 page=0 chnl=15 char id=8218 x=522 y=511 width=14 height=72 xoffset=10 yoffset=0 xadvance=37 page=0 chnl=15 char id=8220 x=90 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=8221 x=60 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=8222 x=720 y=438 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=8224 x=965 y=365 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=8225 x=875 y=365 width=29 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=8226 x=0 y=511 width=23 height=72 xoffset=7 yoffset=0 xadvance=37 page=0 chnl=15 char id=8230 x=740 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8240 x=360 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8249 x=476 y=511 width=15 height=72 xoffset=10 yoffset=0 xadvance=37 page=0 chnl=15 char id=8250 x=443 y=511 width=16 height=72 xoffset=12 yoffset=0 xadvance=37 page=0 chnl=15 char id=8364 x=374 y=219 width=33 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=8482 x=415 y=73 width=36 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8706 x=348 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=8710 x=280 y=0 width=39 height=72 xoffset=-1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8719 x=565 y=365 width=30 height=72 xoffset=4 yoffset=0 xadvance=37 page=0 chnl=15 char id=8721 x=913 y=292 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=8722 x=812 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8725 x=297 y=292 width=32 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 char id=8729 x=552 y=511 width=11 height=72 xoffset=13 yoffset=0 xadvance=37 page=0 chnl=15 char id=8730 x=848 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8734 x=635 y=0 width=38 height=72 xoffset=0 yoffset=0 xadvance=37 page=0 chnl=15 char id=8747 x=920 y=73 width=35 height=72 xoffset=1 yoffset=0 xadvance=37 page=0 chnl=15 char id=8776 x=612 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=8800 x=350 y=146 width=34 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=8804 x=680 y=219 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=8805 x=419 y=146 width=33 height=72 xoffset=2 yoffset=0 xadvance=37 page=0 chnl=15 char id=9674 x=128 y=365 width=31 height=72 xoffset=3 yoffset=0 xadvance=37 page=0 chnl=15 CubicSDR-0.2.3/font/vera_sans_mono72_0.png000066400000000000000000002232241322677621400202060ustar00rootroot00000000000000‰PNG  IHDR+ƒ IDATxœìOhYžøŸÂer–½»$ò&aaÓÙ3,4žYcÙ‡`y Cºg0D =g$³ö²K÷ìÅKƒ}po§Á–!ËlÂÐ`y+„3‡ùM¬¤ûÔÉZiå`²Ö]¿CæMW*ïÏ÷U½ªzUú~n¶¤ÒW¯Þ{õ¾ÿ AAA<%‘H$:r¹\ÎúþT*•zùòåKÖ{ONNN2™L&¨ß‚ ~‘Éd2''''¼uÓét:;;;;AËé5¹\.'ƒN§Ó)•J¥ å4™R©T’a·Ì'AAÄcЀ jÈÖL·­Ùx°öä Š4¦ ‚ ‚ ®@‚¨±³³³Ã[/ݼDãòòåË—©T*´Œ&‰"aFAAÄ1h@8¢pmTrÅF€ƒƒƒƒD"‘ZFí£}ôÑG*û2‚ ‚ ‚€@‚ÀylQùÿ‘‘½×â=×>xÜkAAG Aäˆæ=z¶ßEdèvï5O©çùã%AAeЀ rxJ*ÿ|xîºYqå)ó²È^ô Î?AAAð”6lË&‡gèFÅ•7Јžñç!‚ ‚ ‚ ˆ&>øàƒ‚–!jœ?þ|·~þóŸÿÜ‹ëvãX"‚ ‚ ‚ ‚ ›\.—ëæ$AAA1QeQH^‘½¶$A‰.¼¼Kø<@A€U™õ²x¯Ê'¥Û[W…¨÷ªÊaŽWí¸‹‘ ‚D§Ï Šj„˜ììÐéè©/«ØÏz¦™,›É„yÜ óß„Âa*Æ9'ØïO˜ïiXéovº]¯Âñ²![<^…o‹6loã?ÐMT„õ¾‰¢¢¸#‚t:žŽúzˆsaQ˜ìg“e3踹U¤½·°dnÁµà?ªÆ×nºÂñb Rƽ0et0 ц̳<ñî•› έÇÎíÛ·oó®ÕÑìÀb¡*(x‡žn“(Á›û¦OU=)"œ<ï!ÊŒ›sÔ¸ÁZo&Ëf2^+r^)¤a1@<òn`Ý\ Þ¢jÔ1åù8^ Dƒâ…Â&[´]1è„¿!ó6DѸ939]aQC\y°w‹¡Ð 4Œ3ªž¨=¤9÷uyþÝü¯½‹n”Q“e3¯•9¯”Ñ0ü8ç±æ®ïp²õ܀̯õ¾0—¯È6FÝyh/ÞÃBô qò€Ñ}“¡ó!‘=ª7Jé&ƒR”¬×¸~߯É^ÔøèÎ%vzÈòJ¡Ó¡ˆš,›Éx¥Ðy©ˆ†Á ;Ê“ï¾àZð§F¿òÚݼ¸g¦W`È6]Ý »Q]°VD2ï!$ºONî‘×9aVtXÒÜ,ºI™ˆйi²’«§J—I{)®_6a/ŒÆNŸ ^)u:”P“e3HŒê¾æe(z ~œõüŽˆé†µ ÂÔ3®hÝ÷ÎÔñ Ù¦ÛË&› ÝàÕãmÈ¢I¯š2 ‹°7F$‡Î®é 9*÷;ÊxŠÛjÐl\¿bÜææú9&^„»‘_·b§Ó«h²l&9›¨ìûÐ9ëtŸ ƒÀëüÿNG|Op-èÇ©QÇK§‰†&]ú ‰ãe~+ä~LC´yñ6AÑ=r2AUöû¡zts?½j]õ9f°RëÛð\öÉ…ãpýŠQIë¥_ã]›Ã<ý=n º½‹:½Š&Ëf::£ 7{¤é¿ê<‰æ®…ïH¥R©ñññq7×0-jÌëtb·Ï7ÓÆË(üÉ—m˜A{¬¼Fwþ¿êâ€nƲƒÔˆàô~º­/û|ä-{!%J¹îná=¸TÒ„‚z€¹]¿²ý% ÷7F¼ûË{ø5ƒ·ßÊD&ˆ‘Á ¯¢É²™Œ®(¯½ÿPYƒ¼/"ù¬cèu*0®…·ew£ÔÊæÜ—_~ù¥_NV?j‰¹=Ø4^Æ!»º’h#ˆ¼µ…8óäˆÓª3Ô+'».ôáª3BAw±˜((Q"LÅÐü€µÆDk€7~~Uu®_ÝÑO¦À»W²ßÄzøµdÏŽ Ö"tÏâ¼ð*š,›é@ ÁnkÈ0Ý ë~ìµ×»ÏE§óB6Ÿ~úé§~9u¡Ž@ÑwBî©›g½Iãe$¢›¨ûÕ퀠óÿ! rÏ¡ºªŒª^1(A{Ï9NkSDÕB«j „½¾ý4ðÖ¯W¤°®_žaCvØà­?ˆ\Ž3Ù'ûŒÉ²™ ÄÁà6ì\Ç|uk rzuÿDç=«\~ûZ”D²ëLÑÝÓ—/_¾üÁ~ðÑ8éª1°©Ü ÙõœÎASÆ‹wëL6ÿ¯Q¨•P~~—i8ñF;Ià¡3$N¥ŽtbzÂÌÞ8ÊœWhØ^ÔÀB-oÃZ¯¢ûtkýêÜÓyë"ŒzÞ^,ÛS‚4@” Q¨bÀ#Èïr:M–ÍtÜxðýðþâÎ`—Q÷V9¿øaãZpZÇÆz&Ñéô”áé÷Šæ¿†/§÷A4VNÎy¦Œ—êÙÖº<¯»æD)wZÌOô]Q=ÈS‚Îÿ×µªnŠ­.o§Ö|RU’ tˆÆ7ª‡=,Ôò.ª+Ý´D"‘¸}ûömÈžìdý:·‡²Ö¯Õ¯¡Ø÷¢0F@ÖhPû“›0RÈ<ñº˜mP²™ d¾±ö7ÈY×;9˘0^NÏ!¥R©ät*é&N,T²ã àgºß8õ`êD´HtTÄu2!!÷Õ>/x¿ÃI˜¸}.ª(+A¬ºÑHæÔÒN‰ê!˜7ìsZgˆ¡uAº²õ«ºnENÖµXë×d@kð¾ßN˜¢üð*š,ï;T®+ú}nçä¹Ý uÊEQ5È~Î绊#Á/€Ékâö,Áéž “‡z¾Œ×QÜ¢{­:ƒ¯ uBðX9 9–ý0'V¼0†rZ1Ý9Ëî“CµlS`Í?Öäu:¾¬ßÄúNÖ¡ o|”×Ù¼’j‰ªaÄ­'BåÉ»N¼ÒNp*ò³Ö¥}ýšl $|]D2X S€_^EÓdƒî+¢¹¥µåTTðÓûOÜàÇXÙQ9Gøe€|W§cö:ÕùìÖu ›oN×€h}é4Dó~§ê=r¼œžyt>Èu^É› ¢Ï…Ý‹g²@G§¿O¶1Øç„Êa4µÈýÎǪî@Èmô¾Èö¿rÝýÆé¡b,‚†È‰°ß¨Á ë¾²Ö‰}ýšn‰ÊÝï=A‡!Ù T½‹~3L’Mu-²žsNR¶DóÔ­âEç›Û3˜i•¢0p+: yªç? &­+~)l^íT&Nî©hžèÜó¡‘"‚/Î ™eeŸ°NÃxdŸ û> Þ=p³9ŠeûÜc½×‰Þ*/oÓ`Í{ûï÷Ëâ ù>–|QšNä4í( ¨î-²Ã¤®‡dý ÛçÍ}Þo‘E˜ð¦¸Éká¶Ø’“Ã(ôYÄ^¥â]ô;¿ÞÙœFé2®ñdvk8999ùøã?v›·í…‚îµ¢ê¸ðÓù¾NÇßuª+çßK9UÏ?ºÏK"ú‚¦ ñdW1’9^²}Áþ;Tö?«2½$³ªµºXT aWnL6@ïÔÛ¦ 4”×¾0DrC6ÓL&“ùÏÿüÏÿ„Þ3û‚ñ{Îúq`Pe)ôïñ èC‚·éËJNÆO¶~Eß ¸áÉÅZ›Ö߀›½Uu?ÐY¨ò¬ b¿‚ŽçíÛ·oûÊ`‚lnÖÔ+‚7'ü ½–á·À«g–ªÓÄíø«žƒLX VTÎM+nöD™Œö¹«ª„Ê]O¶n ú€ ÇK´žx×ÌAÖgEc –Y%_ºé©(sQPnÂnà}Vô»žý ™\¼ Â~mhhš.o¯}.³ŒŸi+N¢ÂŒªRß­iVTׄêƒPÙú¥@{U%Õþýn#dÏ!È>ªz°râ}Ry^ê¾ÿ*Ϲ jsèR&½H­R6ß-«ÅµOu£ÀËÔ•ó}ú4äš''''/^¼xáT&(‹ÅƒÁÁÁÁf³Ùd}¦Z­Vu}&‹ÅzzzzjµZÍþ™³gÏžíéééQýM¹\.W( ¬×Úívû_þå_þ¥Ýn·U¯KÈ›7666fýßW_}õ•õï?üðCÖ÷®®®®:ùN;/^¼xqrrrBÿfÍÙo¾ùæÞç/\¸pA‡^½zõêõëׯýú>?I&“IÞë?~Üh4ôï»wïÞ]5_¢F¥R©°ÖþÔÔÔ}õaÂZ¿ív»=>>>‹ÅbÅb±èDûúm·Ûíû÷ïßwö«ÞÀÚ§DóÞ¾_¨O÷‘ñññqÞ3ý“O>ù„·_×ëõzoooo,‹õöööò~çðððpÔ Ù*еçtß•‘H$¼×Eç?1eÎÎÎΊΠô{®^½zµÕjµX¯g2™ÌÌÌÌ äûœÌÿK—.]:wîÜ9Þë,ý©ÕjµŽŽŽŽxŸ‰ÇãqÑ<±Ã;ë>þüù“'Ož°^Ëd2™õõõuèwP¹~ö³ŸýLå3v‚/Ñúk6›Íýýý}ÖkF£ñøñãǼëŠÎW¼sqOOOÏÙ³gϲ^{Ë Ú¬V¨Òcÿr§JhØýnÞ¸‹#™¤‹T*•ºyóæMÞë¢C¨*ív»m]€¼…$ÚpTyýúõëW¯^½¢³Œ]ɰb_§¹üï½÷Þ{¼ï::::â=€ÂŠLa·Ïõýýý}žaŒ<\R+ýááá!kÿ ‡tž¡Ð n¸©T*µ½½½m7.¯®®®òæýÑÑÑ‘UY=ðLgjjj*‹ÅÊår™õ:½wýýýýV£˜ê ØÛÛÛcé©¢'º‹T*•úú믿N§Óiø/zÃÌÌÌŒŸÑ}­V«åÖ@|çÎ;*ã%(ÙR©Tjxxx˜õZ>ŸÏW*• ý»V«Õ®\¹rEŰ_­V«CCCCt¶Z­ÖÄÄÄKÁK&“É‘‘‘ù£BµZ­ª®=UDÊ#­.LX§,'‹ÅÅÅEÞ÷ðž[<œÌÿÑõ펊.‡‰hÿxøðáCÖóYu\täx>}út___ë5¿x"£Å[‘’cEf]„~¹(z7)¼ˆ ÑfÌSíJ²—¬­­­‰¬ýËËËËn®o_(v‚·t*Âv«kÎÚ• +<£Ì³Í»¿"ãš_†¿=Ty3Çvwww­ÿ“YI»ñp)óöBH¹\.Çb±˜õîуB2™LR£ËhQ.—ËVåÄŽ=‚GÕ«a"¼H2Þ½³†Xf³Ù¬ýukĘh,y¤R©Ôþþþ¾ÌKÆ#ÇWVVVüLpã]ôÊûO B6ž‚µÇòư±±±¹v½^¯_¿~ýºýÿ"Ï~æE5ÙÉçóyˆLvèþ'Ã+¯<¸²Fiy…Hù1-Ò6èu*óÂ_#„¼Ñ‡îÝ»wOuoTT”½ŸçH] ;Lxû‡H‡DVxEãåÆÑ-rċ֮Hnž®ñ–ªäð}¿ýíoËú¼õFˆ6¦(z7)<¥ŽwCEVIÞo¹\.Ç:L"‡Šv%à Ïã(òäBÈØØØ˜ý@,2®ùiøñ ™‘„gYÆ4€7PÅçíwæm Ti ›Ü~aÍWÜÚÚÚ²¿nMñp£t8=àÚñ;ÀwÑ+ï?%ÙxNÞK!·nݺ%ж"äÍ<[XXXà]ƒ±Å‹œ“áDaôÊ “Ëår¼H+ÕjµêEÄÑÙƒçù Š ×)$Y´F6777DE©ÌˆÓ•çÀ] GÌñt7ž“J¥RÓÓÓÓ¼ëYÏ&n{v‚/7z.Ô¯p ëæY7>|ø5pN7ú¨ òrò6c‘gÍC‰,ô_ED^gžÇQ–&ÊÝwÓ,÷:)ê"Ër7§È?B¾Sž½5õ’nnð )ÇÇÇǬƒ'õtêR8––––dÜv»Ý¶¦Qñ¥8©š,£R©T u(^{ÿ)&È& o4;wîÜ]C氧ع¥ÕjµTÒ¼0èììììðöß ‘…´›’ÿo%ȵ ;§‰ÖH©T*ñœd2TjÕÈ¢Üä•ÇãñøåË—/Ëdà•x‘Û"ç/ ¦R©TDµÏ ˜0^aà-€ÌòAaY7šÍf³R©TXgè"Ë$kcò«7§**OÖÿ/óøT«Õª“0R²ÃïuÊÝZÈó´‹œöy ±@²,‰¢¹µYø¿è`©+ Lû UxŠ!ßåwûfJˆûÃ½Õ `?¦Óéôƒ¨®ó(FÊÈR<¼Šôyqy{æÌ™3û·û·2Ϭ(@èÆè½ººº´²ÈÃOÙœ«•EˆêsÂ/¬å¦Xg­V«=zôèä½:R­ÐâšN?¯9LÊÿ·Ä:…œÓxŽYqlÙsQ¥V,JAv6”}XѨvxûooæ9wšÍfsnnnŽ÷=F£±¸¸¸(’EFÐã%‹þuŠ=eÒŠ(‰wïÞ2ˆsV/'ëÇÑe ô0îg胟„-ÿÿ¿øÅ/x †lñºÅ>Wx‡‡sçλtéÒ%ß)«C@ÍOûƒä•±"º)EF–& »#Dn›ŸŸŸ‚™züyÞ&kåm]†9§¸)À7555Å2ÈÂÆíF³(EÊЂ~¼§ý ÈÖh½^¯OLLL´þÄ3ëw*@Ê¢Œ eƒ¬‘±UäIó¨Â(3P¨P*•Jÿ 9ÜìÖ.,Ü>{‚^ *È"doܸqãøÃD×P©U#3ÞÉÎJ÷ïß¿/Z+nŒq,OdXnÜv‡0y¼Lâ-€¬…5¸T9á)IT¡åݘ(zm(¦äÿC"<âñxü‡?üáY¯µÛíöµk×®éûŒ÷º]à±È€b·BC€TÚkF)BF!1tuS®µH[ »™RO¶~U`y’Édrvvv–õ~–!)¬‘2¢ö}Öû.2øPE…1@Èw‡ÙÞ)+ÐùèÑ£G¬çÁòòò²›T/xòäÉ“çÏŸ?½'(o¶_²9=xÊ¢@d‘V¼3cX u™L&óôéÓ§AU3WÚÒÎTL^§QÑ¿jµZÕý\–íúp¶Î:—òRYU"PœîW¦—I¼c ú… .°îV«ŽÓœí°†mÊrˆE±l6›Õõ™™¬nòu½ ¹²Òétzrrr’þ-ª[( /_¾|ÉòROÚááá!Ï@ÁòrñBËEãgõ¤ðŸþùçvë¶ÕpÐMd9Z²-ŠÌcàF5‰Z­VëùTéâí=¼õ ‚ç}ƒì+öõë6ò‚µþx÷qrrrÒ~ [«LQN±JA¿\.—=;ì …‚¨¸žì€Ã;+´Z­ÖÕ«W¯Ê 9ÙSDQS&yM ë . C†Ô€°?¯í¸m4µZ­vãÆAË!ìcm:¢¢¼V˜nñKÑ4í¬äÔáÕ­ãå„w ²Ag=¤­Ê»(g;“Édx›°Xƒ»Y›HÏK‘ËòäÚ½ •J¥R.—ˬÏóÂÚ !‚vï†Èº Kb=„ÛívûþçþÇnݶº¥€ŽðŠÌc•4+2//]CUüyáŸÔà(2Ø×¯[ï.+⇥d$‰„= (hïìsØIA¿\.—sÂ+*´(‹Ú …œ¬©¢ƒ[”¢Ÿ‚‚—Â&êÄíã-Šà]jä5^•újµZW)^ç%~y?»QÑ?¯ÚbûÑ…³Ž—ïdyάüp»òÎËÙþ‹¿ø‹¿p+0â§y5¼Ð $ß]¦ÀÚ=¹¬BQÅb±È38!“Édffff¬ÿ“(áy¢¬ž–AäääääøÃìão Sï–:Âÿ)¼üxJ”ÒQ GM&“ɧOŸ>ååZ—J¥TiÌf³Y‘·˜µ~E^A¼{jW2XÞ?+¸ë‚þ^§ýd©d°öZÕœOÖ^ÔXôƒüà¡©T*µ½½½m5\@úÊŠ-tXZïe£B·tÐþO‘Ul…j…iaÍYñx<¾½½½mP9ÜSÒétzsss“õký®¯¯¯;-ôÆj=fµ¸g2™Œý@æWÿv/(‹E§Ï”µµµ5žAÈZ¹»···——›/+´È²ïÛ•&ét:ýûßÿþ÷¼ßÀ«•ƒ¨Ñh4wîܹcÿ<ñÅ_XתŠqR( ÖH¡T*•ÚßßßgÝ×0uh—‹çÏŸ?4ñ Ø­µ¶¼$™L&ÿã?þã?xÏÝ7nÜ`9Èd÷’ÖéwD/Hä´5 ˆ°Õt0Þó?Ç÷öööhúœÈ8X( <£ŽµF‘¨+R§R©TTŒ¤"ƒʼnÇLäð¨ÕjµóçÏŸ7qŸT?GÞÆMµyB¾«øoÿ?$‚ ’Öé·‚9<<<¬jfÁ‹¬€Fd:@3e¼¼ŠnrÓf™Å;QHL<³r›í ˆW»¯¯¯Ïäþ©Ý¤ r †>t ‡8VÞ(Ï@™ššš=Ìí¡/©T*õõ×_m®V«UˆE_4~çÏŸ?o߸¬J-ëóTAµÈTUŠMEgø?¥Ò2™LftttÔþû——j óååäZÛËÅb±/݆—ïËZ¿Éd2¹¿¿¿¯ãPÑjµZù—ù—¬èú¼´!QÚ–ÝPCQõ¸ðBÇí@[²¨×ëõÝÝÝ]'ŸEÞÅͽ äÍ:ûÕ¯~õ+7F0Gꘌ(…bÉçóy¿e6§ÎQalÙ9‡yT,D ¦Eb¡Èî±HÁäë¹!2¬@¢ÍXé}2L¯0ðŽ@Õ"Ƴb©.ª°åƒQx!Ä"ë´ý íæ3h.š¬uz †„ܨT°gû£Þ hÿj¼üi•ê­"Y___ß_ýÕ_ý•ÝHf]#¬ñÇãñ÷ßÿ}Þæ¥€ºÃÿ)nÒÂ+=¤Ùl6GFFF¬FºZ­V{ÿý÷ß·+xV#ë]­V«öörÅb±ÈzŠ"*Xë—%äE@Éår9VaO¯ª/‡Ö¼ yÏk§È9 ÇíEy@Z²ÐÕïù§÷ÂèÆÐ‘:~ z¶F©†nœè²¢²s!ò³Ĉ *;¯M;ʼn³„×áçô³G$Úq¡hÒxÉjé‰Î¢¢H`ÝÎ,åvx‹Ê@G­Å!âb¼pxÑÖY1â‡xw  UÕ«Ë+öW( ''''ªyʼnD"qpppÀS&&&& c+ò6÷ôôôüô§?ý©ÝÀ`_#,ãØßüÍßü ï;£òðö"üŸõ4ÖCWðŽWýÂ… XVòv»ÝæµÙdr“¿á­òÙ* IDATß­­­-'- i×VN²êúõ k{O/aÍ ˆÁµÙe†ñ³Ùlb˜…¶´Ò­Q^ãä^°ŒNŠòæóù<ÞSoEðD©†ndJžYÑ?HëLBä:’̈àDo‚=äÍ#ž¼¼gh\³Ùl–uPmekŤñR‰´†\Oåu˜ï=O1Y@º‘gœ§€‹&°Î Ƚá³ÍIvâÅá.¬¹Š²å¥R©Ôét:ÇÇÇǬp"§Êï^Äãñøßÿýßÿ½ýÿö5ÂòV«ŒÂ†áÿHÔ[i"¬MßÉžÀ2LÊF'!’¼õkmÙ)[¿;;;;¢\bS•?aÍHX"k>Ù Ðz1öâo™L&GFFFìÿ—í÷¬öÝNZÙRL/Y¤0Ï9IcP­ÓðãÿøÇ<§)Ó r°ä½W摳¥g /ŒCd…âM`s‰DbeeeR‹.P»‡‡zä²ÙlVv 7²‹Å¢¨Ç®µà Q!£r¹\¶‡†ßýYy„µo+ËÓ›L&“OŸ>}jÝÜy….­õXûI6›ÍÚJ<뱓P:“Ö¯[ ë?N§!~§ðÂWYsƒRYÏ^eöÝÝÝ]'ãt© ˆ8JëñãǃÞ3ÀÊ9Û ÁK3e)‚éE>;ÙÅ+š,*ìÇój×jµÚ•+W®¨¤„7›Íæ/ùË_Bßoâx‰j ¤ÓéôƒØÏ[*r[[[[,Ç 5"@t=¦ZÀ@VœL¥ç|”`Uf¦¨æÿëjç¦÷·Úív{aaaA·aÇÚöO¦LX[ßèTT:eð >Ð4›¨¤Ç˜¢x‡5  V«Õ=zôÈþ{ÿnV¡KByôèÑ#‚Ë Ë³+¬¼Ð17•ÙMX¿nqÛ;Z7¼¶~ö¹!:¤ò J¶Z­–›–onU‹FDž7•B/QU¾œ)l*+–Lˆ»s!äùÅ+š,Rjyí áw'bÁ*8*ÂÔñ’Õ–€ž·D°R‹Tæ1ÓõpÈòX †„¨mPŸ§Ï}Z[ÄŽ½&‰Éã­-LÔÃ!ÛX †„(mP"k,O™)H:·––––xžŸf³Ùœ˜˜˜ÐY¨£\.—‹ÅbQ×õL2gEaGе…ôSÂÿ)¦D#¨Òh4×®]»¦ò@áµ,ªT*Õ^X™ý;‚ gQ©T*jìv ‚4ˆ‹ÅbƒƒƒƒÍf³©r½»wïÞÕ'‚ ‚ ‚ b4"ïõÁÁÁA"‘Hx-+õ€ÚÏò‚ëŽP01€%ëþ@£üº·‚ ‚ ˆ3D)²^ÄF¢ F dmmm-™L&íÿ¯×ëõ‰‰‰‰V«Õòòû‰Db```Àú¿f³Ù¼uëÖ-Öû+•J¥Z­V­ÿëééé9{öìY/å4‘‡>´ßŸJ¥R‘E4›ÍæÕ«W¯z}oAAç\¾|ùr<-‚ —÷ïgn8Ë{.ûþn¬fÏŠàµû£°¬Æ˜÷ Ý‹¬…h7쥂 aB´oc‚ Ê|ðÁ-A¼G–„iA‚ f!ëþ„Ä ˜Ðåüîw¿û]Ð2¼~ýúõ«W¯^Yÿ7<<<,ÊégÃCAØd2™Ìúúú:ïu¿R¾$ ˜XéNFFFFXiºHtb¿A8­V«õðáÇÖÿ%“Éäììì,ëý™L&3:::jýßóçÏŸ?yò䉇b"H`@Údzݪ /©T*µ½½½ÍË!EåAÄLÐáņ—g!h@Œàþýû÷ÛívÛú¿B¡P°§är¹ÜÞÞÞžý Ë*†‡ nåÝa-$,ð ½ò¦ÕêÐÐÐ fÁ*’M©×ëõÞÞÞÞb±Xô[® °:C²Ùl–õžd2™<<<<Ä”61Ê^Ñ8„%EVDI‡ri忯Ê%Ë)²#+6ç–Z­V{ôèÑ#ûÿ³ÙlÖ*ÇÖÖÖ–ý=¢Žº0m¼P.ï€<`ùnn†ÑÚ ‰(¡ãÁª:Gìß-ú<ëY"j£DçŸè=º~;äªóYX*•J¼y\­V«SSSSº¾Ëo cédõNçÝPK§sšE÷Á:#z¹téÒ¥sçÎc½ÖmޝL&“ùöÛo¿M§ÓiÈûÓétúÁƒp¾±‘ìÅB¡PðC0“™›››µX#äràT©I¥R©›7oÞ”½oqqq±Ñh4œ|ÇÙ³gÏöôôô@ßÿÞ{ï½çä{T¸~ýúõz½^WùL³ÙlŽŒŒŒ8(&Ž!(—nr¹\îøøøú€!äµyßKcD&jõ¶~Î+£j:NÓï1-bzzzÚ~? m”žÚívûÚµk×L »Å0j$( «Ed³ÙlØ¢ýÀn¸¶âWí« Éd2™ÑÑÑQÖkVCy«ÕjMLLLØØâVÌŸ Py›F£Ñ¸sçÎÑ{âñx\6¶KKKK2ã‹K–“ülŠ—ùöâX<¼–ËÔñB¹Ü#zÈRh¯]VÎo±X,Æb±˜›uÙh4ýýýý,¥ÒÛÛÛ+«9Òl6›ƒƒƒƒÖÏ™ÐØMG'ìïïïS£l<ÿìg?û}íÎ;w œ,§0ÇU÷+È\Óq°ûÝï~÷;7Ÿï6L-ú!Ç/_¾|9h9îCgÄ0ÄIÖmˆ")»¥ýŸ¨‚½Xo«Õj ¡ÑΟ *E’håEÊààà È n?«NX^^^–ÎE‡àT*•šžžž}¾Ýn·Ü.zÙ}þíoûÛv»Ýf½æåaåB¹‚8ÌÊÒ !¿üñÇ&yuÀ2(@vœvDqσ¯Ò6éþýû÷yó”¢éiytttÔ ;S€¤ÝBH¹\.ë*úG мsÐøøø¸lîY1¡ƒ Ò}@ gô"«ÒÇãÛÛÛÛXØò;xÏ—niÿ‡¼çÏHÛ®v»ÝG«ÊÛ´Z­¤*?ï iû÷èÑ£G:¼¢ûüðáÇ¢üWHkÝrµÛíöööö¶irbæx¡\pdJÔcÜMPK»Š!Àï^¼««««v%êùóçÏŸŸÏCÞt¤û€ÎêõzýâÅ‹FcjjjJ–.—L&“÷îÝ»‡Æ,±ò«òƒ Û£ƒ* wúôéÓ}}}}¬×º%ÂkNÛ5!NV©WVUTT-ÒM%ïJ¥R‘Y9Ym!mÿšÍfsnnnΉ\vDùÎÍf³¹»»»ûêÕ«W¼÷ {a¥ÉurrrR«Õj¢¼Ü ä2u¼P.8"%ÎKK»è¡–JíÔ Û÷(~F}ª: µZ­¶±±±!»öÚÚÚ!ð0*]ᇲûLï±hƒñ"O[&µxÊ6>§ÕçÊeêx¡\0d†O{=ªìeÙl6ë&*É/ )P~×x°|üøñcUEN¶ß¨„ÙYó ‰@ˆó*Ú?:ZÏš¯M¢"ŽŽŽTê°¨Õjµ+W®\\¥I[$äMý'km'Óæ³iò˜ŽÌp&Rþ)#Àúúúz·EdwSû?¿ÓA!E-­µ*R©Tj_¶šœÚrŠXn¢S‹KP>äaoÇiQ’[·nÝ’µýè£>‚t]X]]]Õ¥„ÈêuíyÐ6c<’ÉdÒôQ¨'m``` ,‡:Ù<%þ{dŠ€(ÿŸUÏÀŽ“(hퟨ&„ý³{­ƒb8‚éVDÈ»iŠvܦO˜6ŸM“ÇDd†³jµZU5²V*•ŠÈN§Ó››››*׌¢Ü÷0ÌèÜ÷ $“ÉäG}ôôýÙl6«UNˆ™ÝZN"÷Êtsq"'7mwwwWµM–v»Ý†„ÕB‘YÔ¬ù§­V«%ºß:óíe¡±v¹Dùã:å2u¼P.}ˆ ŸºöhEˆ Nõ;·çŸÊóNsâÜÊ%óÊ%—©ãÕír‰)e:ö¼D"‘€¤ùDH¾rØ*–ËöÈï”…vBRËTÔ!áÉQiƒ ‰Â1¡èŸÈ>¨Ò«V«ÕnܸqCö¾™™™V6¤h1¤n‘ióÙ4yL!“Édž>}ú”÷lw£üS*•JEt~/ …°¬…7iÝÈZ9R¼(Jl*²¶àa4„¨Ô ò‹SÉ[hUk²Š×ív»=>>>ÎËç¬Õjµ3gΜ‘yÞYy‘ÒiáªZ­V{ôèÑ#ÕÏâMQYš‡=ßYv˜×Ê+“Ë®(š"—©ã…rÁðcß›œœœ„zíëõz½···—%{{{{ÝFù $_Ùć YÈóRPVo’b¡’² Oö² ¦Ÿüë¿þ뿆¥èŸhôj[0H{Áx<·/!ò´Ûíö‚LÓæ³iò˜B­V«õôôôØn·Ê?ô{LIÉaí(ÁêV9ÓˆBß»¥ý!ú zC±:‡!uÊår™ÎSYqË ~H›Y%j•–0ª€R©TRi "k©ppppÀZ„^ôåv:.º[ Èî1«…„“Ï \(—)réh­"²'ìììì@®å¤¿, Ñ>­c¶ÂÚ£¼n©ç£l.Èæ"t®Cæ4ïùfÒz­ QY2tx!k@6®*mûœv Èi—^´³3m>›&b>*ÏUÈ>ÏÛÏtœÓœþ† ž×¢³“›±=/ì¿UUŸ”í“NuIÙYÒU@“)‹E•Bx2 ÏŠ&*øäÔÙh4wîܹ£ò™z½^ßÝÝÝUý.²\4V¸³,O[‡'åB¹¼”+Hd5(²‚cÅb±(óÖ™¤h!ZÁÈæ)!òçƒì÷BÓM µe )kðänò8Â{×M:Nós,ï!ÅÅÅE'!äÐbÅ´;$šIµx¢ióÙ4yó‘¥>Z‘E‹‰¢Fº)ü_†W)é¬z²HU{j+´žˆ)€ *9fA=„ÚôÜ®‚VÞ¥èlûG‘âåŸz§r¡\^Ê$*¶ÐBŸ6UˆwÈæ©ìù +mí9\@R áÉaêM® VػɸmUX©T*²Õt:þÇüÇ#ÄYñDÓæ³iò æmK9RìŠf”ñ#=Š,­QµU´i]NAòÄÌcãw;ÃV«Õ‚Vó×Ýö"S¤x“XfýrÚåB¹ü+H  G=‚仩'‚¸G6OE]+ ‘ *‡ˆû÷ïß—ƒdk ÕÉ»¥ß´ÑÑÑQ?¢Ü’Ïçó:ò¢!ŠO>ùä™—Ói$‚ióÙ4y³¶¿¥ˆŒÅ¢3C·´ÿ ]Žn¿uL7Ÿà'¼ƒ˜ÛÊÕêØ¢ïwĚƛ°2ë—›qA¹P./å ™×—µõŽ€à€ô ç…ùC"AT c,LbœâÍ7ݵ$œâU^ªéQ´à”.GŽŽEnAÏgÓåAÞÆÄýGå^ŠŒÅ¼3ƒJ«H'ãsxxx(2ðmmmm¹ULrJË"Êäì³@€@ZçBÈôôô´n§Ì-Ú\dÖM§P.”Ëk¹‚bQ­ 5""úÔàÞdé1Nú€Ëœ¢u O6½7¹—d³Ù¬I…*­•©½¨†^«ÕjNeS ý·cÚ|6MÄlìiõz½þ“Ÿüä'¼ç5+bR)†Ñ"ˆWœrš34¢ªˆ¢V5"kV¡P(è–Úʇ7ÖÁµµµ5ß/³FËÂ^d^JY8Ê…r!—騲´ØC¼C¦ðrûd„BòNyëžlzor?˜ŸŸŸ7eßI&“ÉÃÃÃC§¤!8)6Êj©ìÓæ³iò fÓh4»»»»ÔH7444T«Õj¼¨1Vj£¨˜ ‘Áa&L5é츩'ç¾DT«Õ*¯¿§J?QkK¨R$‰D"¡N¨;QŠ,«D+ˉs:¡ÝÊ% ½ J.SÇ«ÛäªT*ÞܺÃr IDAT^ÓÛÛÛ+Ê{­×ëõÞÞÞ^§û•!ln µF£ÑßßßBÞö<Úñ¢V‰ÉÈ"0XaÁo “Jàî2¼0e™Á. ½ÉÝP­V«ù|>/{_:NONNNú!¯#ææææT šmlll@j˜È0m>›&b>üñÇV£Ž¨ $+µ‘WG©ç $mÑÈ´Õ‚æµlmmmñýN[’----ÉZç°¾‹¶ÞQý>;T…B¡ Ê呵$å4y)—l.%—©ãÕMrÉ0!Ç,ÌVlÐq cÞœ,ƒõŒ€Œ‡Ó±ÎX¹ìƒD”ÛMÕëõúõëׯCÚ½bVåæÍ›7½*Œ i H©×ëõåååe]ßmÚ|6M$|ðöv{ ‰(eç â%Æ×H¥R©ýýý}h¯MSH¥R©éééi'ŸÕå}PéQêÕ¾Þ(Ê¥‚ß}㻩åRíÙ>Oež 7¹ÀbeöPSHxrT{“·ÛíöÂÂÂBëO@:ó¤ÓéôÒÒÒ’nYXF¨BÞDgggguËD¶5Ó=OL›Ï¦Éƒ„ÑZ²¦ˆæ ÎÄKNA ÉÚÚÚZØ”BÜË­ÃÒï—⤚§r¡\*è® SÌ0çÎнv»Ý6ùy#Bµ^…¬ Û(Y]{ôŒ,<9ʽÉoܸqî^©T*œw/ ó²¨T*•ÁÁÁAˆòí¥LÐsËÌÌÌŒîv‰¦ÍgÓäAÂ…ÈhlM!áÍ›n ÿ‡€}€"‚jÇ•Éd2££££~¯[tÈíÖÒ GÓ…¬-Ž” åREE.2Å,Œ!ê& WBÔ‹š„j½ ^^'Å­wâ­¥2@Ö+¤Ú´½–DP¨Ô (—ËeÖûWWWWe5¼ö¸[i4k×®] J&•‚Å:S)AÌç0Ƀ„oÿá‘hʘhÞ8yV:™á1ŸÏç½$XN"÷l8Íw‹¬}^»Ýn{=™U&t"‘H¬¬¬¬ÈŸþùç²½+»ŸyÎ*óåB¹Tѽÿˆ"üòP]WÅ d /!á®} «`5”CZAº54AŠ•Ñ0eHxrT{“óÆæMˆQ„À[òé–)•J¥nÞ¼ySå3ºS$L›Ï¦Éƒ„QñØ?üðCѼéVƒÄ‘€èá!fz¼d¨jµZíéééÑQ…V'““““²Âõz½þOÿôOÿ${г Í@ñ+?›9ü‚rQP.5 rAu.p‹­UM(\(R’p‡ïÉêX U²û¥ËÐ$‹J aÊ2cz·ö&‡FènÏ+âÖ­[·džgQ‰D"qïÞ½{NösÝ©¦ÍgÓäAÂÅ“'OžˆÚþð‡?ü!oÞ Áˆ ¦hêã!òG„è=€CYÆšÍfsnnnÎÉu½Ì[†ZÑWWWW[­V ò wÚúÇïûÅêmÊåzÊ¥T.·èQ‡ÔUQM«2½5Ô öCìðA×…lû)O.—ËÉŒe;;;;ôýÛË—/_Z#4J¥RÉdƒ\Ô€8Xí`û?>&¦¬‡•S„ˆÃT¬èhM§79 ^…ËrÀ!dcccÃ^³ ÕjµdB•p?Y;'ž8ˆ‘HvèE¹P./å‚àW @ˆÕzx7µJ"‘H@+‡ÓÔ'¯åòÙ<íéééùþ÷¿ÿ}Q”‰îHYmxØ„yܽ¨|¯C&§‘ ÃÝÆÆÆÆý×ý䌢ë|hÚ|6M$\¨ÎŸnñ«½4bi9°&“Éäáááa©T*y/‘" ;àÛ{·Bø·û·=ô!ísD=`!yˆ*av2Eɉ…b$’W¼’Kv˜J.SÇ+ªrAð+”bõ‡Þ!ÆE?¡^§ãããchîp½^¯ïîîîz-›×Èæi<ÿâ¿ø…(BÆ ;$Œö&Ôãîg$¤—QªgÝg¦ÍgÓäAÂ…JÄW·Gd‰ˆB¡IüÙ)vB) k¸Ðááá¡(*›Ífe!GN§sppp@mQaBQ=ÈšSñpAó_Eu æææædÖ~ȃRÛi˜³,ôFd\ñR.Ùæ”\¦ŽWåÒ…®ð2¨Õ?›ÍfEÕR©T‚zØuC ¾ö}Z¶ß³ˆ‚÷Ÿ"›#¢Ê΄xÓ AåùmÅÔz:Añ¸¢^“Ç ÇŒj´X±ýÌ9£Èö3(¦ÍgÓäAÂ…J FŒ ~ñg¤Ø‰Ÿˆns‚½hcMã³ö® :11 À‹.kkkk2ëÌ­M›Ï¦Éƒ„ •ÖÀÝžã.Š0ÕÑ¥Évvvv ŽñN§Ó‘9âñx|ooor­“““º?Ÿ²^RÎ/d´­­­-k¡’D"‘xðàÁhHj6›ÍÚ‹£ò]xëÞÞÞž,’bIo·Ûí………ˆEOG[@/ò³)+øØØØË;‚r¡\^ÊÅχ‹Ê~šN§ÓÇÇÇÇt“V ¯7™z½^¿~ýúõ åЉÊaÎŽ—ùP6[M½Ëòòò2dÍúYIg—HD‘èÌ]µL›Ï¦Éƒ„¨ çŒ쎠—· Ђt~!³žnmmm¹90³B\UÂ[!mÿ=zôÈ^ø‡Û¶€‰D"1666&ú¬%’oîܹs—.]º„r¡\~ÉÅEUùM½^¯OLLLD-œÑ©7o=ªÅ¦°7ù»@×l2™LÎÎÎÎú!“®(hË?Ñ™:>ª‹X˜6ŸM“ tLãEú\7sÊþZ­V»råÊŒ:"êõzÝM¸&ÈôÙl6çæææT®ë¦- ¬Å!î7Yøj<Û•,” åòR.(ùuS©T*Õjµêö:·oß¾mJt„r¹\ŠêÃÚi˜¦×›&d$AU¸—íÑ&íÍÝ^A7L!o‚CCCCƒƒƒƒ^xСT*•J>ŸÏ;ù,­bëæ¬|8H z½^_^^^vòN[î@#· bÅ´wi@¹P./å‚"+`è•rÖjµZNŒÔˆiº'½Z­Vc±X¬¿¿¿¿BT÷Ã+MOÀJÓb Šž/7§Q¹\.)&ªrf©Õjµ Ùûܶ4m>›&.D~4‰£4± ~¸ŠÝ:T.—Ë~f¥R©TÆÇÇÇ¡—v»Ý·Z®*•JEÕ˜Q¯×ëgΜ9c͇ƒ¶ÐqÛö Úr‡”…΢§À$Κ§r¡\^Ê¥‚¬E¨—ÖejPUÙ?«ÕjÕT…º^¯×{{{{ésajjj*h™üR¯ÂŽ)Œ±7¹h€®‚w F'k@&“ɬ¯¯¯C®¯zfÅÓÕ‡ióÙ4ysuA£‘ØIƒäÏUúeí DÐ[¼6 ;;;;~üAü£T*• ­Qü —Ëåx²xyXF¢ìÙ†Ï7$L˜6ŸM“ ¼ùã÷ùÃT2™LæäääÏE‚ ˆd7VKP ¢EÔFAL´ùlš<%xŽ4 ‚ ŽyÜ;NçàààÀ0^ñ’Ñûì3™ÇEæµÁƒv F. £D‚ióÙ4y$,ˆÎ_Ö5Ã3 ÷Ayúpa=$ ›NÃ6‘p#KoÁƒ&L›Ï¦Éƒ aAf8³XEšÑ¨† ‚0x÷e`ø?b:Pc– £DLÁ´ùlš< ‘3¹\.Çë™‚ ·´0#aÀ­‚‚ÞIÄ$L›Ï¦Éƒ QÁé 3Þs*hA‚bccc£V«Õ‚–A¼¢Ýn·Z­V+hYÄ-¦ÍgÓäA“X]]]m·Ûm•ÏT«ÕêÔÔÔ”W2!‚ !ÇMZ˜‘°àÆC‰¡Éˆi˜6ŸM“A¢„ÊúÂõ„ ‚HÁð2¤pª `%b"¦ÍgÓäA(Â+¨‰©˜‚ ˆ2ªF´0#aÉ‚‚ó1Óæ³iò ‚ ‚ D‡8lõ‡„ ¡D¢„ióÙ4yAAAAAAAAAAAAAAA¯ø'4G;•J¥^¾|ùÒþùƒƒƒƒD"‘ðZ~á©I€lôH$К¼}£ÓÑ_;T*•d5)‚œ Ý0Oy ¬`7AAŸàe2ÞÁ•$hd )¶LÓ‹©ˆâ©²ç馛æ©ì·²æ ‚ ‚ â¼Hžç‰w°ÆN!ˆ ˆÚÚFI©2 NÚRv:þºqžŠ~3>?AA|Bt€·Êx¸¨X‘p! ùFÃL3È®ÿÑG}¤"¯tó<0‚ AAÄ'ÂphF"¯oÔ•ª 1Éc&ÎS±ë ‚ ‚øDÂf„…H©D¯¢·˜dPMgâ)¢^)â8O¿Cd­AYÞÐÍ7Û Å(:î;Ôภ‚t7¦ÎB¼s7ž³½Ç@ šâ<}›HÔ“Q}hÚ {›ÓqâÙ膃MØÇE&¿I›*¤ Ôê)32b>.‚ N1½u‚XáÍWœŸÝOsÛÒTçÂyʆwïtŸßEzº}žˆÞûÎýrkètBfñaWt½"Ì㦈Ù8;Yû^\AAÂÄ|ðAÐ2 ÁòóŸÿüç^\÷üùóçu)¡8OÕÐ9ö"}Á®#(§iè0p/QD^Lû Q 1²fE×KÂ<.P/d~x‰h3étÜ)ê²û×í–eAA¤{áé ,›§›²Îê§X_V¯×ë½½½½1ƒƒƒƒÍf³Éú\:NONNNêûÙzÊår9hYL" ã’Ëår…B¡}ÿÍ›7oá O$‰{÷îÝK&“IÖëÍf³9222Òh4N®_«ÕjW®\¹Òn·Û¬×³ÙlÖ ƒŠñEDh ½(© ¹GVcéã&3zu:hœBA¿€œE¢ø\N¥R©éééiûÿÛív{aaa¡Õjµèÿ2™Lfttt”uÅÅÅEûYýÏ«Ò?444d½¨•F£Ñèïïï¯V«UÖëï½÷Þ{Àß…(R.—ËÔ#ºGÝF˜Æ%•J¥nÞ¼ySå3Éd2¹¶¶¶æ•L<––––Òétšõš[åŸ"3 …‚é c”™šššâíõÑ=¢Æ–½½½½x<'„ùùùùn‰C!3HwSd-‚X±*ýÙl6+{6›ÍFÍA3;;;ËrÂmlllÔjµý;‘H$VVVVè9ËJ¹\.W*•Š6¡œV¬Œ ~¦ˆèöûÀÃÔq‘UQ…Åû¹¡‰Ò‚¼ÈÏýnÝE1@ H½ 뜀¼ßä±Ã3ÖMñ‚¨*aÐ4º Ÿ›QÃiÚ-î;ÎQ}Žyu]”C~]]g4ùM•âŽNŠ2S \¸páëÿ_}õÕWN¯‰ Q&—ËåXVÌf³Ù¼zõêÕV«ÕªÕjµ7nÜ`}Þ¯T€L&“Y___g½V¯×ë/^¼èÖóoG Çã++++Q<€‡V«ÕºzõêU^ê!o¢T;Nçøøø˜9BùðÃ?Ô/)b2å•s˜ŸŸŸgyŒo ÊÀÖÖÖ–“ÏS§ÉTqÃÎÎÎŽJЬˆd2™ÜßßßkAi–÷¿Ùl6çæææ¬ÿãE[u Öõ• Ô"ÁRdªÕjUk˜‚DžRÍ ¥¯T*•|>Ÿ·¿7™L&ïÝ»wÏKEXFT¯×ë^¥XˆŒét:½¹¹¹éÅ÷"rFãÚµk×x©šÍfsppp0‹Å¦¦¦¦tʇ‹ÕS!Sn¢¦FxiÄ2™LæÛo¿ýVf…°µµµ…QHÔØÙÙÙѽ'ùqnö‚L&“™™™™±ÿŸ•ËÏK`½× ×À U><<ÅuíkkkkN 6ˆ:²n:nÀ"¹HH$‰ùùùy/¿czzz:,†fV5Vè?!lCA½^¯////˾Ǖ Ç····Ã2¨â7<ëÜ7nˆ”J±X,Ú«°{‘Ï“ÓoåŸÂ3èØsŠÅb‘gŒ£˜ÜFÒO¬lTòVONNN^¼xñÂKÙàH$‰<ÐÎLÈ›uýÅ_|F﵊Bô#ꦣ쪂„ÉÉÉIè¡…vXi³V’ÉdrdddDÄÞÁ‹f…ó³œv²¼+® „|Wü Ãøä]þøÇ?þñÌ™3gÜD}°¢Mfffft®·ÞÞÞ^–œA¶U¬Õjµžžž»L/^¼ø½ï}ï{AÈFvvvvT«˜Ó*¼‚~,‚j]‰ø/UÈ èPðQD?<úNÂÞŒ V íãiT!/¢°R©Tz{{{ëõzw0æEŒ³t–3K%Õ’kà…¸ó¬,a¯¶ˆ ^ðìÙ³g^(ÐÿïOèºÞ¯ýë_¥è«ÒjµZÏž={´"h{­ Û‡Q q:Nýõ×_ËögªøËBýëõz]ö¬ÙM¼ÌÕ4Õptttt–ýQ„×ÞhämTº,äóù¼õ¬=888(ê¼b%LáÍb%‘H$ÆÆÆÆdï³÷½g!ëX4000€Ñ2ß¡P©T*±X,fK&Ä܇7‚ ˆ_ìììììíííÅãñxÞ{Î>ÖåŒÞºuëïáiÍëúÕ¯~õ+YªÖlˆ~Ëf³YÖ•ÕÒQšæÃ˽ ~x£‘ïH¥R©áááaÙûhw»‡¯Ñh4úûûûYgm;a oF;§OŸ>Ý×××'zO³ÙlÞºuëäzF£ñøñãǬ×úúúúNŸ>}Ú‰œQÄq ÀÜÜÜë 8<<<Œ–HAº ^‹Ô ”`^ÛIBÄ-¤FãÎ;w¬ÿ+—ËåX,ëééé±[à————E!w*õ*hÿx,je.¼vC,輡ŒCÛHz׬¢ ËZ)… ý÷–?aýßÈÈÈd­Èæï¬mÇÔZ¹\.‡)ˆ^½zõêõëׯÝ^§§§§çìÙ³guÈxV?-‘ôÀ(¯\“dAÌǤù‚²¸§T*•x­Ð¼(Ú(BÖrªZ­VE][nݺuë¿ÿû¿ÿ»···7wx‘…Üò&Gusss*ÿÖÖֽטB‡×º—âöžJ¥RÓÓÓÓ²÷ÑHû¼¡5= Ít:žœœœt*«eÒJ) ˆŠlýïÿþïÿú-O i^N§ÃJ‰ä×ëõúîîî®è=F£±¸¸¸(»Ö¹sçÎ]ºté’ì}A@£ÏpOGìœ={ölOOOè=,š¾È[È»¸.è7ÖMR•:N§;NÇn¡’,~QМnðÖñ “òŤù‚²èzýeùð~Îd-§ áÌF£1999 Íy†PyaÝ2 …B®ý ïµèüí™Ntë~,;ôXáNöQ¨GSÖáäúõë×EQ#¯ 6Aóày­” ÷:¾ÖC+·1¢´z½^ÿ÷ÿ÷wsýnƒÚhšë=ÐðÿÕÕÕUÈ>»¿¿¿/‹ˆÇãñÙµ‚†îé''''˜†¸…ž!-ŠÃ²Fü±ºÁé‚>tE›®Œl6›ítÜ[ M’ÅoŽŽŽŽdaœccccN”ôË—/_–çdž­“IóeÑÇOúÓŸò¼þVªÕj•>ï¢êìív»}íÚµk^„3W*•Š,§z}}}ÝÍÞk§Ê²ß¤R©Ôþþþ>Di®V«U•ê½ôÐ£ÒšÑ 5¢©¬ˆB^­V«2y«Õj­®®®Ê®åE:!4ô¿Ýn·Âö¬a±¶¶¶Æšƒô7!W˜P1´ój6›Íæþþþ>äûEyÍVÂPåœÇã{{{{a5î"Á“ËårnÎÝΩL&“yöìÙ3Õ-/PeSƒ’Ëårƒ6”B¡PpêI6I– xòäÉ“çÏŸ?½Ç©·óÂ… D¯·Ûíöýû÷ï«^7HLš/(‹^._¾|Yôº¬mnX=a­È<³Daæÿðÿð¢½Ag*„Õ›m¢'I‰a¥Z­VUæˆÎCtÝ@ þwïÞ½ ùÞÝÝÝ]Y€îtB•ÐHÅé0 êØ•ßè‰D"qpppÐéÀ"Ó¬@œªyÍß|óÍ7²÷„µÊ95—#f’ËåruY¯×ë41SkÁuN¢v˜’…[<~üø±Nô&«’N§ÓŸÏË”:YÁ6UdJ¡}ÞCBè¡õ T¹yóæM/Ã÷ƒ."¨¢hªzþuß ŠlÝ@<šªÆˆB£Ë£)ò„[‰JË?Bĵ¢ÐÙ@'Öºn×X"‘H@ ¾Hú¥µÊ9¤¾„—¸Ý{­µ~‚6ê"f!ÓÊårÙ¯ÈË0£­ îüRY%k]@ŠS™$‹ @ÒTó7ß{ï½÷D¯‡)üߤù‚²ø 5‹Bˆ™î +Éd29;;;kÿ?¤Ž€*VÇ¥ˆ¢©ªüC<n­ˆGSU¡6ÓáÑT»¨(Æ¢Z: ÝU’½ÚëD@ `V^¼xñâäääDôž¨9£FÝ0Ô~A¼Gä`Äè&8Z ^„»Bû Ó<ÛH»!Bä)“d1Hø¦J¿ÍD"‘½'Láÿ&Í”Åòù|>*¡f^¥hX™žžžfÝŸb±X¤÷zpppÒÿŠÕ£äÅAÒ+åŸ~!7ù|>o]3½½½½ªû„¸[7ª Íëׯ_¿zõê•è=:ú6CÇ.*б¨ÖA”"œbí:ãÖÛoÍ/¦kúôéÓ§ûúúúôH~*•J…îEn ¼ØN‘¥ ëNC2§ho^ÕÅÙl6›ƒƒƒƒÖOjyeUÿ„öÎçóy‘áajjj rˆ2I¿Ý#Y@<ËŠ¤Q.]ºtI–;lRõœ»á–E'tŒ‚â@ˆ?žfB`÷§Ñh4úûûûéá1ŸÏçu~¿Î"‚²Œ'Ê&“ÉŒŽŽŽÊÞÇ‹@iµZ­¡¡¡!Èø±¢3 !Íív»}ttt$»¾ß@Cÿëõzýúõë×}És&'''y¡ÿwÞÈ IDATîܹӭ‡cHû>Ö³¯S£¯“õ1˜"¦ «jÌçA£»0* »€Ô£A ƒ—ÄZ¼Ü%럛¾½&Éâ5*÷ÈZ!×íïåòw:ÁW†Å¹-YdÈæ£yíè®  ïÐä§\nö9Èþ£ 5èAæ–Õø͵u:_ ××ÙwÞ>7 cíô™oNÓy ÷±ÓQ¾èÜ{ ÷Cå÷‹dãÉÙïÂêm…îå2Tæˆ5Â@Çõ(Ð=Þ«°Ôй—‹Ú zµ©^åxûºõb½¯²÷;=W˜2~¢­€ûæÃ µ²‡øAÂÁëõz}yyy*K£ÑhܹsçŽè=¬Öu&ÉâNï!°4hþ¦¬`àÉÉÉÉ‹/^È®£ œ»Ñ•Eݨ¡áïª!Ϧõ–ò¶ÌÊøøø¸¬PÅÍ>G½ÚN¾W'ÐÂ|år¹ì¤²<4rfuuu≼uëÖ-Õè™°†4CCÿ£Ô÷›Ûív{aaaÁÏ(:‘qGWß÷T*•úì³Ï>³þÏMû>;år¹‹Åb~vté6¬{¹Û´/l'}dµ0ÒétziiiÉO™ÂŠ6Ti²c‡„ƒC;V !ëö0G“dÑ®{D‘ýH˜/¤ýŸ×9=8w»G™L&óí·ß~ë´íS€vîÈçóùþþþ~Öš¤)e¼sûœ5•MG®)h­§Ê?!°pÇz½^ßÝÝÝ…\b8#Doû=ª…¡¨„þ«MFô›ý2rX#DãOµNÇy„G&“É<}úô)5NQo¿›ö}„|×>,‹Å¢Ð2LXÓ¾Üu­ÝÌt›ðP( a(¨4Ž N•&+t£µn²²vC*½†­@*¨Ú=&Éâ¯îÒ @–9àÞ½{÷.LZ8w»W;;;;nóBU™šššb9”!óð<óvì <4ϼ\.—!õZ­VkuuuUö>¯M/‹ BÈ÷¿ÿýïCÚYºQþ µßS­i㪫ý^@[6›ÍæÕ«W¯šR[Æ ¢Ú~UÅÞÙÙÙqRMŸVxWI3нg³Šù…ën'Në“!ѦÑh4?~üXö¾­­­-4ˆt*MÖF+k7ôêÕ«W¯_¿~­úÝ*öCI²@ðëQZ­VKVÈflllLô;¼R­àÜEYX¯Ñ\2hØ» Èš¶1¦U½¥¾Õ^)šÖ5ïE‹¯¾¾¾>Ùx¹Uþ ‘¯'íQ!†[kú´ø˜ ˆ*àÛ‰JË?BÄV¼þÔëïv•u³†öëØ³uóCüAgñ@$ü@‚[[[[a­_âßã½P*•Jn[¤¨V<†T¦­œÜÈ6Yxqìܽ{÷®èaLó|Yá‡Üp'áÿ8wQ:æˆßÈ*ÍC½ó, žxUO3Ðð#²"•J¥ö÷÷÷ýîå-‚?*µã¤> 5܊¤i}¯dˆC¥ZúÒÒÒ$ü;*-ÿycÈœ™™™a½æÇïѹîh‡]׳Òn·ÛW®\¹‚ùüᅞˉDâÁƒº9m¯[ÙÝÝÝ­×ëuȽ/ …±±±±‰‰‰ 4ò½ÍŸ#üö›´h°d1ñíïïï‹ÂkEy¾âRKŸ‰ãâ7¦ÏÝ °ËB½UaSþE‡|BÜå2CÛ¼©zš!žc7sÅZdÌ /¿[L.F$˽·îÛH/­J³ÙlÎÍÍÍù!“׈"Úív’†ÓM<þüù“'Ož-âH!j$š´Z­Ö´Fu2aJÀÛœ¢Ê“ J“IÕ†M’Ť{d’Ãó.Ê<²ð“ÆÅ¤ù‚²°)•J%ˆ¢Xþ~É%#•J¥¶···yžt?r™ÝxšEïauáaoÿ£+uƒ†{aô›™™™qZ²vœ¦ÏèFå>RtF8tkè¿(â!JÝ tA/‹ÃAjÓ Î±¶v ›!ÑG­V«]¹råŠJ¡HL x›S‚@Úª4ñÚÀÕó0¬¸½G>›'2™—~xxx˜u¿eeáÿ8wdáÆT4­ò³WyÿSM{ÿ\¯Š~ñ:è ÇWVVVÂZP¢1 較†þ»I“1 Q ]ÐÝ Úív{|||<Æ@wQN§E‚­üV œ¬¨1=jíi yÛøë¶µ#jµZíý÷ß_e?) 4¼á”¨ ]iR9ËòU8Nî‘õð¬rp¦yU]YË;Éÿ—=Øqî"*\¿~ý:¯5]µZ­z©:Å˼±zwܶó²cúñ³èW:NONNNúñ]^Q*d];…æèèèHvŸ ¡ÿA+Å~bõtË€(T´@ŸÖ!šQ{zzzxÑÖVoù|>ïô7R#ƒŽýÏúûÜ*a*ši2Ö–’~wéAÂC£Ñh\¼xñ¢¬í°4¼á”5ÆÒd%—Ëå04G^Ü#´d‘ÈÒâñxüòåË—­ÿ“õ†‡Tÿǹ‹¨ÀÊ£‡H¿k>ð"LìÈæ"ë`ÁÄH–\.—Óíݱî ^Ýã_þò—¿”y nÞ¼yÓ´ñVAfä%DacGG Xhè»Ýn/,,,`(ï(—ËeU#j¥R©Äbê=ßËårÙnd ×êíííUQìX÷T/s†U fõb-ši¯ä7N,«ãÊM]kJØê9!ê´Z­ÖÐÐÐJꦛô¼¨pŠïZlèP&Eýhe˜d95IBôÞ# –2ÙAÍÞPÖv ú‰s÷]P>µZ­¶±±±AÈà™3gÎ`ž¬7xUÅ^„=ôØëý2ŸÏç?ýôÓOEïK&“ɵµµ5•k{]DQH­Ֆ޲°ÐÐ̇÷ºæÜ¬5HÏwëÚ}UÜFò]DäÉÉÉ TY€ÍT5˜AZµêÞOý‚ÄÝF}Ñ(/Q ]ŠÅbºæãñx|~~~Þk™Læ”ü-p‰DâÞ½{÷¼¬ÆlÍÝÔªõÚ$Y Ø ßYõVžžž–y¯d"ûAUÖvLµå˜[pîv,·nݺ5111½Ÿß`’¢ék?o?~ù|>OÃi["Ñû³ÙlÖ”*ÄNÚïÉŒ¼´å+äû!)`²ŠíÐh­jµZ5­®G”øÍo~ókÎÚóÝŠ“µM£b1wýããñx|ooo¯ÓEOÉjf¢n0ƒD ˜R‚5BÁMÊ%ÔÛoJ7”Ã[*•JQd­OÕñ¡Õ°¹¹¹éÆzgÒ 0I7äóù<}Š6Ç©©©)Þ¢I&“É‘‘‘Ñ÷Ȉnc%¬ãÁC› —ËåìVBÞxz3™LF”ûégø?ÎÝî“ÊÑÑÑ‘Lñ²æ…È=R‰Ô€x¬xVkÚRØr·!^òx<_YYYÌwˆ'Q¥Í`*•JMOOOËÞÇ;8µZ­Öêêêªè³¢êʪý"Åúïf×$´êÕE¼ÇÚšP¥’¸ˆòY»Ð9ïwä¤×XÓ¼tò Ïu•Z-ÐýÔ®X¢rE;p<±·³²VKµöñ´"+¢i‘e ·rŠU>ž²`’,^Ãk£òûd­g>ýôÓOYs‚ÂË©óœ»Ý)‹êu¼–™·Ã$½ü{]Ÿ:¯å7{/ºÖVV" óÒÎ :–kAŠœÉ®Ã»†l^È~d^©Tlw‚®½Áš-Âë‘9º±g6tóî7tžè8St:Þϧ@繿zO!{ôþ²®…r|w-ÙÙ§Óq~þ<ï¬{› ãXƒk]¼N•(È„ètœ Y×ç=àL’ÅKx›´j¿pèxñðë·ãÜí^Y ¨þT׈ÙƒÂ/EzP“=Œ¡ý§M=°º5Kèƒ_×ÚÞ_èºQUæeß/[GªŠ„nè|…쬟±£r ¯Ÿ‰h#ÖžUÞ;wçŠÊqû ò§cçÐçS§ÃßSñú®¡bH$‰/¿üòKÈsº~ìë.èñ¬›µ€B6+•r’mB¢œI²xO'–)U«¹ÛïsÎ]”‚Ê&µÑËÖ‹Ÿ;•µËº?*VÖX­àÑ=@‡€ÿ#*(ö—Æ,‘\*Èz×½UqbètÞ݇Täôž$‰ÄíÛ·oë>,¢@ÿÏÞù„¶•e ÿ¹ø³ÉB^Xö¦"·«VUe9Í44®&DÎ"X‚0¤ª!=‘¼¨ª€³ˆA2$›™éžE ‘À^Äà®4Ä x˜ª0H/…t™Å@¬¤2n+ÆVÖ"H{}‹ôí¼zõî½ç¾wï{ç=ߪ*–Þ=:ïþ;çž{¯Ï»Í ~D `~?:÷—:êÈÞ Éoßî°¯e¼õº—pëƒaë=n  ÛÆÁ¥ºÈš| ˜dÑh x=¡óºQ ÂF}—d¢{!pëߢ±Æ)¨ÅÏmn‰›ªKÈ<ÔØÑù^T#x0]GÕ k$9¼áß¼9Át¿Å~‚èì_ú’ŠCÕº®wÄY?¿ðÆ–÷bOI%xúý~ÿÊ•+Wt–Ûi·Ûí7nÜÐõݬ­­­yÙ£ª"+CJr„ ¯Ṵ́èCÙP©T*Î<››››&6ªµZ­¦+#«_0É¢ƒJ¥R1aü[–¼Ó™0©ï†&Y @ʺy!gv®V«Õ°Œ±r¹\ö[›GØÙÙƒfcccCÖR©Tj}}}]ö,“cgØÞ Aè$ÿWÜþÖív»—.]ºdb1µÑI­V«ù-ß§,Žn’#¤¸…¾‰Âü„QÛ "4–„I¯ðÂ[t†«†½™…¡¾K²xEWȯ³¿ñÂÙ‚Lˆ$Bwœ®$u¦Ð}@õwA¯\éÖ“©÷BW`Ôëõºé$©~û†°í¨£ûz½ÿ˜šSTÇé0ËÆUsL¶ Swß Ýw?ì¨N¤˜dÑ%·n\u³gÚ衾K²øÅ¯A ¹o†maÐelBÞG\Ð~®2ë2&L¾—avt:äýØßyX¥3É:ּȖ CŠîuÇëXV9‚v¨ÚXÞKè„iDé–AÇBŠI¢Af¢}UÝxM8hJrcê/$‹w¼_"ù*•J%ì„0"¼:?TÞI\–e® f WÉðÀþþþþçŸþ¹è;Ç;dsK€xàu,Ð{0‡ßùEר69‚røÝ_ay/¡Áˆrݘ±¹Æ$‹Ñb3, õ]’…ðd¦÷²qCïEP@ò¯¸Ѻá¶ÑŒüæ‘à"3,èÝÔØ3¹JrðÑ}µNŒú0F#Š#Ú“Þ©ïA~Qq„-+AAŽ®Dj„:AÞ÷#Ôw ‚ 'ä ‚ ¢Žr@"Ü»wï^&“ɸý-•J¥e§Ûv†åªAAAD\!AAAA ä ‚ ‚ ‚ ˆ!€AAAAAA–EI ‚ ˆèCAAA1€ ‚ ‚ ‚ †rAAAÄ@‚ ‚ ‚ ‚È@AAAAAAAAAAAAAAAD¤H&“Éýýýý‡B¡P°>N§Ý>ÛëõzÙl6ÖoÑI½^¯ótâ¦F6›Íöz½ž T*•ŠH&F½^¯ëh…B¡ ûÝ•J¥¶œAUœûŒýýýýd2™ôò]Z— ‚ B€ŸâÇÐvªºt1€ã¾èÊúê`ÀwÌA!Æm¯YWy{CZ“ ‚ B€#ú}²ßìöÝããããt:ö#“3ªJ–%ŽÎСo‚ ‚FÜö1²½oIë1ARÈðcTOÚeø Ç—éûóÏ?ÿ\åýÅ‘@%d‘ ‚ ˆwð¢ ÝDq=„ ‚ˆ<äø1n†%Ójt€›þT½§ggÇáð]Õ AA¨ãe¯#Û£Aˆ À;Ütávš ð{Í3êyQ<ƒ8Î ±È ×è‚ ‚0‰'@œ÷A±€ïpÓï$Y¶8ú]UBðìðÂñâÏsÈÐ&„ ‚ ¼#«ˆä$ŽUˆ‚ ˆãæ"»Ü~@žA =Ñæ9'â¼0ótgÇAA¬:EÜÄp"‹ŠŽ³íAZùꫯ¾2ñÜ>øàƒa3†‡ñ7A}œŽ•C 7§ åG"t½*DB‚ ‚ ‚  N#zÕ–w½—„.dWÈíЕ\‚ å" ¢†ÛõÙâêì·ÔW ‚…Wc ¡”ÝÒ|Ù¼Fé÷ñÄmŸ‰p«ÔŸÃ2ßDÍH–­[NèJnD½XÝ/» h}`— aéC´õ¿aq`ï§¢ÚÕvt…­a×Ъ2a;dÆTOM'ô#Øý5 ýêAÔ?ÝÖ'Ñç©/‡GP€ Æ¦hupppÀk;uºÁS!îw '::F@˜úÀ(ÂÖÔëéÖžSvY_Uù­ÐùÉÄ3UŸk'¨~êU>†ê„>T=ðºaýÃÔæFõ¹*!‰ºñâÄQݧ„ed{uxFY¿!ÃI½€¸88ýÎ/^û‘®y>ªN:]ˆìÓ}Ô¤ è±)ú-lÿÂÛÿ²^˜pøUVtmšü8H090è“XÀ ÕÚÈö¾è&¿lÂW1"¡†£Êbц÷"BçZÑqú @t^ë¬=Ÿ‹úXÜ cÑÜ·ß Åqá$Êûf]zð²?Õ1¯EY÷ºàÍeA¥&ÖȰÆ&OÎýïsÆû¢iïG ÝzòêaÄâÀ¢,r`‹>üž";‘Í*íA&UU£ÃKL?è dÙû Z䈦ÀœAž¸‹úWÔ÷>Ü<®*Fï»°ÈLúp“rÏŸd#©q û=ª#ïy˜Þ‹&ŒÿÁ€¿@†¥rDÏÀë›<ýð>oÒõ«¨­!^õ¥8;Lï£bšÒCP9ª†eœBÀ0–u¬‘aM·µº®ûù®2A;ƒèLl–e~㵋>°ÈLúp›À½ÈЉOÕàe–á駟꾦a‡§ß°ôA€h9¼†Cª†]ú×VÔ9t†&,LE1Ù‰Â>Ùä<‡¤™Q#ìÓËò¿F†=6Ýt¨Ò—y¿ßÈ|† *;(ÝD%L‹>°ÈA6é˜T±éCÅ ú *žª1«{qû}ØÞ‹ßߨ‚Û» S䈎€7–¡s%¯oëÜ@éhˆ<½DÁ˜UÁTäTÔt¦2¯Šæ5·µ!*vB\Àpú/“җ›_}õÕW&ôòÁ| ý@6e&²fGÁèõ“\eƒ•,úÀ"„ QÒ‡)T¢Í…CÕí=b~/ª‹$o¾V1¶ÂÔ9ð8dû‰(l LÉWæççç±®ªx‰œõùP’iºNªÎgLäQ¿Ò^ó³FÒØTD§À²à5ì „þ]Æ*ö{HXôE]z3ý|lú0…—‰Ÿ·¨y1½¬a¼cZež¶Ëêüf}xÑ×,Ý@Ç f'"AÈPuœ©ÌŸÎ½8ö=2dÌû99þ·û·#@p¨æ\1…×µŒÆ¦t;, ¶Ç*5(dMèÆ—ôÓ9 ˜vDM¦Ðåùõz2êÔ/æ÷5Ö¼xýÙzâÔ-f}xiƒïP‰˜Áê@$;lpÎc*}Ýo•'ÌFdŽ$g_tõë £µ½®e46=`‡ÉrÒÝB6¿¦KžøÕ7}`‘Ši@ÔôaªŸÊdÿÏÿüÏÿ„´Å{_nß·ã|Xß ÔÁágK§Ó鯿þúë(èƒ'?9ôµËб‘4½ÎEM,ÄA<4Ð$¿Bý¯¨~/¨0nȦ³Ë}ö¨`òô_´žz=„q“ ûØD‰ @Øáˆ~ž2ùMJäõy /§žƒúˆEXäPÁ¤ *ú¢ŸÊÚ¸{÷î]H’!·çôz½ÞÝ»wïBu‚ù½@æ{Ýw/1ëÃëóÉðh4‰ŸMwPë\TäÀBô!û ö½¯®kL’Édòw¿ûÝï ŸW¹¶fÿM¦Œ­°Þ±Ê)°ýwyÕƒ×1`'ì\¦NÿUtã·Ob›Qëïш¯_¿~ýæÍ›7aµ/âôéÓ§GGGGEŸét:½½½=ÈóÚívûéÓ§OeŸûì³Ï>ƒ< Ø­­­-Èçd2™ÌÉÉÉ tpcÑ9°€]A÷SúÓŸþtppp`ÿ·D"‘¸páÂû¿}üñÇ;¿{pppð§?ýéOж0¿—ÉÉÉÉD"‘}æÉ“'OšÍf"Ìú ¼“Íf³W¯^½*û\¿ßﯬ¬¬t»Ý®êó1ÌXäÀBÔõa7–U~ÃÜÜÜ\*•JÉ>·½½½Ýn·Û~dìv»Ýú§ú'Ù瘱T*•JÐgçr¹œC(N§Ïœ9sFô™Ç?Vï~©T*•G=’­oŒT*•:<<<ökIËËËËn:ë÷ûýÝÝÝ]Õç±>©2¶üöIŒc3 qœ:uêÔøøø¸è3GGGGAOP.\¸pA6‰¨:0^¼xñBö™ÉÉÉI™×´P(T&9¥R©ñÔbÑ9°€YaôS''''GGGGÎ?wîÜ9öÜd2™£êÀøá‡~}f|||üÔ©S§x¯T*¯^w™L&óðáÇ¢…‹>°È¬ú«ŸÊp3lí¿…ç¸T5ˆ1½ghh.—ËÉž³µµµ¥¶&:EÁ¤B÷îÝ»Ùä5F¹\.«<ËüEˇ\ë:ÁtËÝa“>dØC‚ONNN¼$N óØÁÁÁÁ³gÏžùm t òÈd2™•••ÕïÉ¢Èz½^ïåË—/½Ê¥J¡P(x5þ-ëm$àþð‡?@NãÆÒÒÒ’Ûïî÷ûýµµµ5Õçùí“©T*å%2ÛØŒ Úét:½ººº*ú fOL2™LNNNNÊ>§êÀ8:::ê÷û}ÑgFGGGOŸ>}Úío~'9™L&sïÞ½{nâ,r`«>Âê§Ü~[*•JÍÍÍÍY–»ã²ßï÷Ý"x`z/¼ˆ݈Â÷1éƒÐC¡P(@IN§sýúõëªÏÆ0`ÃnÄŠôÍv~îŽf³ÙìóçÏŸó"71èC†ý.óááá¡Nƒ:KU3g IDATúƒ2¾øâ‹/T 6Ùi«[4—Èå7a«ÌÞ€H$²¨å¸‘N§Ó—/_¾ìö7/§ÿºúä¯~õ«_©ôIlc3JhuÔëõ:dâÕ}Ç4 'Pv^¾|ù²×ëõDŸI$ ·Ž¬k’‘Ëår~N‚ÔGäÀõÓwð~Ûи…Ç›:Íâ½,,,,è8ù’¡ã^Ûh 2ÆoܸqC¥_`™?0ÈÝKÙI$‰G=R ¨×ëuQX?}ð°'ÝÒu5ÑjµZ‹‹‹‹–»ÖjYêó˜’ÉdryyyÙt;øà7cñéÓ§OUŒ,ï%¨YÍ…E„ c¼Ñh4jµZÍij-Ëìü¦Ìhò{’ÆÖ^b-û½xY[XÞ Ã~š¬ûJ‚]þÙÙÙYvbIdª-æÇn«Õjyyn@N[_¼xñ¢P(¼Da°‘Њ6gÏž= y®n=DÑé¿—Pù0û$¦±5­Ðh4öIÕŽJ¨Ü;ñÒ™Þ¼yóæõëׯeŸs†WA'96¨œ›®n·Û-‹EÙ3R©TjiiiIö9'Aê# r`úéOq 5ŸžžžþÇüÇœžžž†|Þ/A¼—™™™·ß£Ñ\4n£4ÄÓ‹“Ëü¶ÐlÖPX¦sç~z/>l}XÖO“øé3f4†ÝaÁsZAª§u÷ZݤZ­Vûmö>ªÕjÕ”|ÿ÷ÿ÷wîܹãç[[[[2'$¹¬e™ÕC­V«Hh4 ?m˜@4Ϭ­­­©†Ê‡Ù'1Í0ûÔ†¶Ûº8:Ngjjj*ŸÏçƒhÏoR@&¹V«Õ:þüyÑ ­Õj5È»|ùòe»‹>°Èlú»ŸBÙÝÝÝuÞO$‰Ÿÿüç?wÊï%W –÷Â+í£H¨ }þ€†{-ù‡eþÀ"ÂÖG¡P(èJâgYoûçüüü<ÛŒGaOjR‚ϲäÉ7ËårÙ‹!‰èúôÓO?Õ±öˆ²óCóÛ˜ÒC”E¶Z­ÖÎÎÎŽÊóÂî“„wŒ9ìíÄÄÄ„ß;¢Ãˆ(LÇÔc·±±±Ñét:¢ÏØ“¢„(õSÞÝñ .\pþ[ÐÙŒu’ÏçóNÏ3ä®Z­VelÆèèèhÔs¹0 aà^Jþa™?°È…¸èÃ>§E}΂†;Cîp¯­­­É©†I"‘Hܾ}û¶[NH„[\ô Q¸¾—Óÿaê“qØÀžØc0xww™€X*»v»ÝÞÞÞÞ–}ÎC–­~ÊËà†jmzì@Âá)QáD%ôÿÖ­[·TŸeþÀ"‡穵©©©)™Q­‚ýÊV}È`§LGªe(̲݀õÂë5W{(<$"z-«Ùl6Ÿý·¬ðû$–±EËÀ F]aAXª¥-ÜB LNNNê®ËKÄ—¨õSè½~*C ;Ùl6 ¹ÏÛét:—.]ºäe¼`™?°ÈÁ`†¬èÔºÝn·'&&& ><˜“Án,cÓ‡{ò¾8GœBê«ä­ùöÛo¿õ'‘§CFÕqµ¼¼¼ìì£Oå·¡ ˆNÿ½îw¢Ø'‰·šвÞ&÷PI¸7ÌÈ–—;ÊÏž={vppp úÌøøøø©S§N©<—^¢ÖO¡§ÜtN 3Éd2yûöíÛû¼ª%ÿì`™?°ÈaYoÃÖU Y–€j~~~^%Œ¶Z­VÝœ ˜ô!Ã^MGµ”\œP}'GGGG&C®Fí3ÇäÎ÷ôôôôÌÌÌŒýß }S%±¬i=`]Óét:ªÏƒT…ÀÖ'‰w€ÎL©< èR©T¢H1ååŽr·ÛíÊ&ÆÑÑÑÑÓ§OŸVyî°Éò©âév–M’-iQûéÞÞÞžìô¡ÓétööööTŸrh*ܼyó&$ùš—’ ,ó9ÜNâUi6›ÍÑÑÑÑ‘‘‘^â=ûµ·¶°èà ¬”œ[Ök¯@æNÓ'&Þ /'Ž Õ@e%à‰Dž£'jzÀ‚¨¢Çööö¶©¨ÓïÃØŒ*Z#˜qq8k¾–Ë岊!äÊ0ùY8N"‘HÈ&X‚0©~ )!—ûÿö»mšÙö“4Ã|Â6,d³ÙìÕ«W¯Ê>×ét:ׯ_¿„Lª`YçTäøÿøÿЙ¤Ž·¯ 3^Ðï¥T*•ØÜu|||Œ¹ê‚H~U åT½I(×ív»äpöˆT×o“zÀ¯2×ÓËŠ^Ÿ$~Œ‘+µZ­&sø©åm,ž@“œ°è‹XÀ¢,ýT…n·Û}üøñcÑgŽŽŽŽ¼Ü‡Ãò^, ^*I•LÁ˜ôAÀ*ôß²ðÌXäÀ}°C%]‡7©T*uxxxÈqKNmú@3•:HDå¥òGX§ÿ–5\ófÔ0–`gggGÚsîܹsQÔ^<ÔPOYÃt±è‹X }ð‘Éo2MPïE”ðÇ+&®FP?Å4ô¿Z­V½†þjÔëõºéLÔét:ýõ×_­ãYº±—5å]kðKN= ½^¯Ç«;¹—LW'ß¡bôщ¯yx§ÿ^òx`ƒÆ¦wŒ9 §lnÉ=†&£ƒEXäÀéâSÕBv°¼Q¹?¨ž`Ñúïµä'N§ƒÁ@T†‘]Û°2²ÙlöùóçÏu‡óšÀ~­A5á¡géjûU'H$Ó0\„äp°,µ:È3é·wÒétúÌ™3gÜþ-χ›Þ1Z@vúå—)CbRS×á6XôE,>ܱ—Ï ã~¬é÷23333===íGF·ç›:- ~>ÐÐÿ~¿ß_YYY¡™æ¨×ëõÃÃÃÃT*•‚~ÇnÀªDÔëõú£GA®|`ÞðP¥´„L&“¹wïÞ=Ë‚;2Eó/ƒÎˆ†¨¢R&ŽPciiiÉmé÷û}HþìÐØ| 4ž=ékàe¨nüLõpBj_Ú™œœœ”-°öl™N„W‹>°È,úÀÒO±€å½ðBþüàå´‹>9ÐÐÿÍÍÍM]2,ó9Ø©¿èÄ‹ à%½K&“Éýýý}^t}¨`wêŽÉ® ªÇ¾ÖÄ;ë& z}ÓD2; ¤ÓéôåË—/»ý-¨ÓÓ}2*c#¡;0™¼T“’@A%ËP­¡ŠEXäÀBÜõ¡»ÖoP`x/ö;³v É´x¥)½– Ä ,@7+AS( ¥R©$û\£Ñhø)Q$Xæ9ææææTNýe8“Þ1NNNNtçÄ{év»ÝÙÙÙY•yOd3y­ÕÄu*/†/$¯ŠªCW7ªw¾!eQÄôé?–>öØŒ*äpawwwWv¯Lµ3A&Dû½)Sõr½ÔPÅ Lr`ƒ>0õS,`x/< É ?ûì³Ï rAÀ¬Œ˜JXÄsˆ¤Óéôêêêªìû&Jþa™?°È…¸éCGAÈ<æ¬Y4A¾¤kªÈæsûÜ雪׋!eQCtúpppðìÙ³g~Û01Oxé“Q›1êÀÞEwR Q;ÎÍ9¤^®j‡†Ü vn±è‹XÀ¢,ý XÞ‹ÍÛ™3gÎ謕Ya`¢:d¼ð"ëëëëSg¿%ÿx`™?°È…¸êÃ~WVV®Ú´¤éåË—/ëœ?PÃWåx1|!z0]¶râ«âÈÖíôÆ€(šH¥”¯_°ôIË276£ŠQ@Øa@Nxa±ÎÄív»ýôéÓ§²ç-///C&9HXŸ[ØÄ«¥z¯åÂ… dƒË¹QÄ¢,r`‹>°ôS,`y/n<{öìÙÁÁÁè3©T*µ¾¾¾.{g91ÌúH­ê†2^Ü6Í…B¡¹oÞh4¦Jþa™?°Èá¤ßï÷ççççÝö-ºßÙfXõ¡“Z­Vcº”U€Îc~çOÃWeßíÅðÕ}âÐuö ÈzöìÙ³ªÙl6{öìÙ³Y£‚¨ P«Õjíìììèj CŸÄ06#I6›Íöz½¯Æì`0Ôëõº‰çê¬a«›B¡PÉ‘ß^ÊG„½äŒêw¡:ôó< úÀ$0èC?•éÁï<£ú| ï…G¥R©èÍ 6ï;× ÌúðÒ†Ÿçë^½Ž?,s †ù‹öqÂKàÇ:ÆÜp+!ˆAaÁ’$:ç1èØ ƒJ¥R´%›íú€´- ý-ný²†@ß#¤ßšœ»êõz]öž Èžï§|'‘Nu/,}2ì± %ŒþÀÅ„:8ý¡Š@ƒlR€N.¼Ž™h¡4ȳx¿‹>°È,ú»Ÿbs`y/n@e à ¥SVçš´>Täö¢?F±=»Èúèg­…è2¨5:ìù‹lžñÓU6²¶ÂÖF sL· #:Þ!{v?†/¤Aúd.äÍA:Ö¨3œ§A|"špæbé“*ß ôM(±u¨l,ü§«*É«0ˆ'èb.Ó%ôt@4(0è“XÀ °û)6€eáx/TÛò»v©l¬H ¢ñ‚eÃ"0Î릑ÝabÿÌ+:æí?þñ”ÜÊ¢ªLõ ¯ h½ðõ×_­¢Œ'¾aþë|ƒú»àäØÄØ@éˆêÂgYúõäu@ê^½¾,úZŽaÛD¥ŸbvXž÷ÂC÷&.*ýÔ –Ža…Ù`YxÖ9,r`Û¼nHlSM™‘ã7ìzÿƒ>ø@‡±¥{>ñ2Ÿ{5Â*•J¢KìžLAºbê“–ÜØÄØ@áˆú¢ÇÐqÏEÇ`ÔõÎünš±è#H9¢°!Áò^‚ì§Ø–…ç½ðйD©Ÿ: Ú`YúçÙ˜Áî°,<ë9°€i^7 ´–eé5 }ÅëÊ UÃ7ˆwåg­V훬­¨;Dk–JÖ¦>É0=6±õ0&Q óWÁëD‡åÔËæU·>‚#J,ï%ˆ~ôY<‚2 üöqU9ý¶§;»q†_ÃKU*ãË:‡E,`˜×M£j<ùujzãUÞ…ý7a2¶tŽÑîfŸDÝÀ{ÿaÚb˜ú$ô¹"Dc[ BÖɃZüe0¨’CXôE,`Ñ–~Š,ï…ÔA¬+£¹W£"Žýª‹ OxÂËüE,>~ ÔàÐY B4_»Í¦Œ-ÓÏÖM”du‚éôß ‘/¬¨¨0Æ&AAŠ@Œa¬4‚ ŒDÙðÕI˜Q_~ÁxúOA¡H˜Ý0lV ‚ t@€·@®š`8Mw"r\`uXxx/l‚ BÆâââb«Õj‰>“J¥R{{{{qß°AoOÀý„’'“Éäòòò²ìs/^¼xáµ SÌÍÍÍ¥R©”ÛßÖÖÖÖºÝn7h™‚ ‚ ´ÍI€ñ´† qˆ°‡À«: wÁ1†Ó‹d§Ó‚ ‚ b…(\ãF #qsØ‘9T2·c4¨Eë %¯#‚ "v¸mú0nÒ‚ °g€NÂÊVO&¡AD¤(—ËåF£Ñ`ÿ_­V«³³³³tç‘ ‚ÐE§Óéllll„-Aè†ADä`I‹Åb±\.—Ö‡ ‚ˆý~¿åÊ•+ív»¶,¡›ÿ¶A¡J·ÛíÎÎÎΆ-A?®]»v­Ùl6Öƒ L@‚ ‚ ‚ †ž~¿ß¿xñâE2þ ‚ ‚ ‚ ˆX‡$€–eYét:}|||¬#áeÐ'‚ ‚ ‚ bG\NTQü}AAAAAAAAAAAAAAA^¨T*?‰ÖxwÔ1ßÛ®×ëu?¿™w}?™L&MËOAAAž( 7ƒ¸^¯×Eßã˜ÏñQ©T*¢ïe³Ùl¯×ë‘ñO¯ AAÄ0À3ˆy†-ooãŸÁ‹àíãð› ‚ b ÏKKŽ‚ ‚ ˆÃ3ˆ.ÏYÐëõzÙl6æoPATbÏù›yº‰Úo&‚ˆ-¼Å‰…nA1ÌÈjÎ …Ïfû7¨"ªGßëõzŸþùç2„ý‚ †^ˆ– Š ‚ b˜Ä"dwç1Óo&‚ˆ ^.yr ‚ ‚fT â8 ¨‘ñOÞî ñWMvCøG¶¸†¹…4ÁÞñ3- ¦ßjYë`@ ›ˆá…ÆG|€\uôs¨5ˆã´o‚^ƒÃƒ "6¸7*Ù\£nü`™F†l“äæ&Yâî``z¯ цÞAAXî'"c†wâÕ…$ϱ^–xƯæ¶A…¬9eÒ!äĉôÉ“,Ì1ÈMTP² ‹À²ä›d á$†ÑDæP'㟠‚ÜE™Aéf ÅÁ 3òx¿Ñ„kEL›¨ e&€eÉÊûa¯ÉXj–a¦ÃåƒB¶Ï 㟠">¼¶QàÍ›7o^¿~ýÚþo££££§OŸ>íöùd2™œœœœ Fºà¨T*•\.—}fkkkËíjÄÜÜÜ\*•Jé’%›Íf¯^½zÕíoþóŸÿìöï™L&sóæÍ›ºdà±¾¾¾Îû­F£1;;;Ûív»¦åÀ&KܨÕjµb±XäýýÎ;w09<0_—!âGÔÆÇ°sïÞ½{™L&ãö·V«Õ:þüykÔ¯k>‚Φa·/’#(ç|ùšdAé±jRS4^«\ørÉf ªUìíx‰ ðªöNx éI^´© :¹OвD1@Ô¯¡1Ѧ&ìS3•k2n8çQŸ Êq€!À¯^1EyAå]›ßgRßqˆjƒcŸÏëh'j²`G”—bmmmMõ¾«èÎóÕ«W¯åü€äêðÃÆÆÆF§Óé¸ýmyyy™…y'•J¥ýœú˜|÷~À2>ˆwðrÄè¸óŸÍf³ÏŸ?®3÷Œt:ÞÛÛÛ ²M,í …‚é(¬z½^‡8ët®A©T*õàÁƒa®+¹\.“ …B¡T*•t¶ŸÉd2>|(ÓA6›Í~÷Ýwß%‰DPm¿|ùòe¯×ëéj/®$‰Ä… .„-ᬙçMâæ]å-2oœ¶›WúÌöyïÓ­ß©8x{]ÞÜiz^䣠æc™gúú•hÏkºÿÉöÛ²ßnj_%À`_ È€Þ‹§£Ÿ¯¾úê+Ïýàƒ>ð³áùå/ùKòø“,˜1yú/Ão¡²ñæmdÜžÁÛìñI ‘qq òõDÇéKÐ^Æ9Ô1¹V˜6‚ÜÞ¥i§CeÞ ª}Ù\äüÄ úL†i#4ÌèoÝ€ òÖGè¸QuÈC¾m[´É~¿©}AÔPûr §V«ÕF\ {Öúùýïÿ{Ïý¿ÿû¿ÿósÏñ¿ÿû¿ÿ[§<~À$ fxõ®?~üXg­k'~û›QN;Õjµ:111Ñn·ÛοµÛíöÄÄÄÄÈÈȈ[î ;kkkký~¿ïü÷D"‘X^^^V“žà!ÊW’L&“qÒµ©ñ‘Éd2''''²ÍYÐÉ:ÃÂÔZ‘Íf³gÏž=«òF£ÑpÛGµZ­äÐ¹Ï²Þæ ššššrksddd¤Z­VUä»}ÑzfYïòŠ@ J¥R9999‘=siiiÉþoÉd2yîܹs™«ÕjÕþ›ççççÝÖ'™L&³°°°iƒQ,‹<]«ö3˲¬sçÎsÓã… .ÈîÞ7Æèèèh³Ùl:ÿ–Ïçó1;Tž!{wºBnÉ FÔWÈð•h^Æ{¾s3ÄÜÇæ/!ø¦Ûwbê †Ìˆ‚´+3Z }Ȥ£2—¸Ó: _™ Îö!2CEÆ´líÕýþ¢è€Èƒæ €— Ý ^˜÷v!2`Ðɬ  =¨±¼¼¼ì¦×ét:aȤƒS§N}¦Óétööööt¶Ûl6›Ož}úôéÑÑÑQÑ3¿ýöÛo!m·ÛíöÓ§OŸŠ>3====333Ãû{­V«‰Ö´T*•Z___‡ÈW¼\e1«ÀéyÝrckkk‹}ßÏI “E$»ßg÷,é8¡Á ’!\t„Øê ‡&=xGt?u{{{ÛíN¼¬z€l^¿~ýúÍ›7ot¶kYüÍ…Û=Qâ§,...B6ëÎdÓÞ  IDATÖ @kqË`øááá!ïÞ.ÛÓèš+ìF¿¨]F"‘Hã•~øáÝÏìõz½—/_¾„|¶ÛívŽŽŽ Ïžœœœ9Þúý~_åy/^¼x!ú»ÓáÆõëׯ‹ú(¯Í°ðÙgŸ}¶ ?qÔëõú£GA<¹PJ¥Ri0P÷êW*•ŠŠ,,Ù‰ŽŽ…A$0@zðïô_Å;eŽŽŽŽL$XÛÙÙÙá° §Ûíve'c–õvãuáÂ… AÈ䕸ÏQ™L&sïÞ½{^¿_( œÁ q¯Nv¢Ò&O‘#bZ–e=}úô©šðKê M²Ç`IújµZ òy™jYrÃ’qttt$“[ä“ìÔÞ$2gˆŠóÁ²`Y›H ·ˆ¶aâÌ™3gÂÞZ`kkk ºˆW*•J©T*yiçÎ;w0gYTÑÉ0w=¤Óéô™3gθý z2A¸#2`) ÆÞÞÞä¤ÛÆ7H°ÌQ^³8 …ÂÖÖÖ–—6K¥RIÕ Íf³¯^½z¥}Á#‘H$îß¿ßmÓ ÿ‡„€›b|||üÔ©S§ÂhCû¦‘„*NögÏž=;888}Ƥñ¤Û ¶,Ë=}úôiÈge‘]Î}Ùš }¢ËsëÖ­[¢È¶L&“¹yóæM¨\Qå›o¾ùÆmm7±7‚æbkIàe!Æy¡P(x5þ-ëíBõ‡?üá^=ÞA€ÁIA2à‘qÖ¯LN˜›Rè<ñ‚( €W6‰xäî¥e½}ÏìK¯sT«Õjé(½æ¥Ìe:N¯®®®ªÊm§T*• B¡PЉ!b<{öìYò8 ÛVûìê‡j_Pº‘½Ý!ð*µ «,nQ,Ýn·ûøñãǼï¨DoÉ¢i¼8 tY‡ºÝnweeeEäL†Á¿üå/á­íaïwÈjAêX(Y;²Ä,a‚¡&&É€G ÄUét:}ùòåËn£Ó=ˆ¢°$¼Áäî«ý$Qõ®h05G•Ë岊#@õò7¿ùÍotH¬®®®ÊÚÍf³Ù;wîÜñÛh. SW á×¥R©d¢tVØí»¡ãê‡e½ºT3ыĤ³šG2™L>|øð¡,b†7Oïîîîê0veÑ4®Ñ@#ZšÍfssss“÷÷aIÈë3²„ЦQr°{A<  GDÂõõõuÌ'÷–ŒH†èÈ€ÒŸ¸ŸþcAÆ>ì÷ý @B/'_ÐìΈÂU.—˪ ª™Èö³ŸýÌŸdïÚ$§Óéôýû÷ïuòoY°,ø–e®¯ÊN_íär¹œî|a·ï¤^¯×½$ÞäÁrkñ*S@Þ¿î´Dt*@uÖh4¼¼²« cWýìeÏ¢zýM¨±±±±1ì y’açõ::NgjjjŠ-вÒ-,áH±X,ÊvË€(ÊÒíÄÒ§³ì‰“ õ@2à–¤¢Óÿ0CRãˆ(ñOKºyùòåË^¯×}ƹñ…惨ÎQkkkk„i~2‘;!*IÚDa£÷Ûßþö·Î9£ßï÷ÿõ_ÿõ_E¿U¦ðÅ ý¡#CVO¢3Ëzks,...òþ yÏår9·dš,bC–#Ä-bQ-¡z]Bw……v»Ý¾qãÆ ÑgL°¾ ¹í*ˆœ„a^ƒpu°ÅibbbÂK¨I­V«ÉBéÜî@ëÈV«Õª³&)+{¢r—ÒNz ðÈÀên”t@zPcnnnŽ·!^[[[S Iª‚Bäí§(ýx­[m ë… IÈ,Ë[BFVSÝé a5î!‡ÏûL>ŸÏŒŒŒ@22TK‘©}gN솟r“a¶ÏJÓÊòf±wÎ;ÁþãÿøÇÑÑÑQÙØcQ L^/ §QÔÕ¯´Z­ÖùóçÏËö ²wËúq‰M$ú€ç€9KT£% óšê3kµZM´/†„€<'¡j4™N~âÈçóy™§‚Ì#êVÓRG¶Ñh4DòACød„¥’§  =¨‘L&“¼d]­V«µ³³³´LqGdÆ¥_™z—ØyB# ± ’8ÌQ&s+\»ví/çä‘á=ý¯V«U‘áï„9=yû*HȰéÄe*ºãÁ߉Êbl_”€Õ²ÞEâ@ßy¹\.Ë"j;Ngccc*£·•¹\.gw@®CE±X,:E”Ëå²§™‘¢+hÄ”¨Z’_d‘UqOØl6›OžÇ~B«r79¬öE X‹ÅbÑK$‹¨åååØÞÞÞöÝ#rÊ«–×4YÕ~5F6fݨÕj5]×’eÈÕ1¨nU¯©@ ùyP¼––õKhËúñIÄ‹ ÍØ-ò´`D÷’!Ú2` êz Óÿð•´ k¡‹;Íf³yñâÅ‹ÃäˆÚM‚¹OîLì¥óEDµZ­†\D­V«é<}…dÁÇо3 €]Ãñb¼ÚqËËáåôŸ9ÔD ÃNˆf‡wW_æDñ‘Ì"7dï9–H$÷ïß¿/êG~˯C¸uëÖ-‘S$î y9{Âêû¡:ì@s•,²QÊŽL„^D§ÿ?~ÅÓÿ(Á‹Â¢(s4›Íæ'Ÿ|ò –ëQF÷éb§ÓéìíííA> ¹OîLìÉŸ4,%OkµZ ZQË‚5LÂhŸE°äØ:®áر_ñzú5¼F‚XÖ»¤~~ʱ÷.sDˆ"@Üžç–k¢^¯×!Q„~Èç\A¢+’¢ä®¦; ™4í8ïø¸a_de ®jSwˆLëdˆ–  =üÞ*?w 8¢(,Š0‡J&ý ‰Òe¢®¹J4HY9çˆ.†éPÄÄ8ØÚÚÚ‚‚a´_«Õj*wÕ½à7LJ贺ßï÷wwww½KgŽ­­­-h¢Âl6›}õêÕ+]e™#BÔ¾J5·«€º³Þ‹ö„€¼¯0*%qvtAö dÁUM"¹•-(= øeÀéAŒ¨œè°œ^`@€%Ô3®Ø«U„Q•"Ês´®½Jž“eÐ û'ÓYø±ÂÆ.CüÎ;wTœ—a·æ cPq&*t#›Íf¿ûî»ïLÌu¢ö1Vƒqýúõë"‡ÅÕ«W¯F±ŸCExùMX.—˲*U##ïJòº:êõzR–BWŃnYðŒÊ"‚ÖÉ€[ ä,///»-Àtú,¢(€¸güÅ+ç6222¢34™GÔç(h",ˆ‘Î0‘TPÓYø±Ã q¿‰Ù‰DâöíÛ·UCuÃnµZ­6???ow7†Je Ë‚©f³ÙÕQº7—ËåxåÓétúþýû÷eÆ?»¦á”’`StG^v¿2‡…î«‚<ûí^9ÁΜ9s&È}Ñét:}|||d8F0èdÀ#H0èô¼°ãT*•ZZZZ ZÌ@OžU Ï ‰ÃM„¥r§ß²Ôßä #;U5q€¢DVg΂°`‰ÙüœÊû Õ »},8r§ñI ®æ$…~<çõúúúºÈqØï÷ûóóóó¼kò‹–eY««««níw»Ýî¥K—.épð¾~ýúµhLëˆ(æ„€¼+Aï‹þæH§Óé½½½=S% ¢= xdÀéïôóýBì¡Ýv ›I–Z†Î{¤¢Õ¢>G±òšÐDXOŸ>}JÎD8KËÚçRÕò†:•…Õ>ËO`ŸóGGGG½†ßëºn‹|>Ÿ—9ÜŒ4ÑÁãÚµk×d:îv»ÝóçÏŸÆ"#±Ýn·?úè£üD´Z­Öõëׯ{ý>”n·Û]YYY9âšPT))Èd€sȼWÃ= xdÀéF:NŸ9sæŒÛß°ß/Œ+¢p7Šø1§OŸ>=:::*ú ֻܨç¨L&“9999á%TÉ€­£œ^œˆƒÈîÎBC§§§§gfffâÒ¾i&'''UŒHU0S‘P²ûé–õS#MV…£Ñh4 %!™òEF"‹2ñaR,‹³³³³÷w÷w¢ß£ëJïÀ†±¶¶¶׊M¼IAŽí÷, æ½rR,‹¢S$çŸ(€A$0@z€Ã»»KöpE„Qú+°WŒw¹‡mŽ"gâæ[ò›à*òù|ro*¢!ìö½yÿãããã§N:¥«M“ŽPHB=ûïI&“ÉsçÎ}^µ‡hÍ´,˜‘È"Ldy_:Ngjjjjdddê¤Ðq¥¨P(D×ÅZ­VkgggÇO˜á% 2Iò{–«!kYïî°¨t&&è}J;aëdÀ%H0ÒétúòåË—ÝþFöphÄá>«. ’É»Ü^¦9ªÕjµU¿9Ñ´q±$ha`vºÝnbŒ©ž‡4™šê;Jûª@ß¿ a—!–EØsZÈl Õ|!–%שŠÈíʇ‰‰‰ çu&YßR­jâ$™L&eIþâ|úoYâr¯A]|ϲ`ƒ­ßï÷/^¼x1¬´jH8¥ z ðÈ€Ò :ýǨN±‰{~Äd˜]_±ãwãe‚a™£Z­Vëüùóç½è?lƒÆt>HFö¨„­CB°-ËÜ; »}/ÈÞ¿Jÿ3Q\ÙúDFX•C  ~e[XXXU‰Q¹2e6666xÉñ‹_üÂtûïA›eYÖÁÁÁÁ³gÏžA¬’ÅâATpªYD1èdÀ#H0D§ÿªº!Ì « + Óznnnr‡^5”Ô4Ã2G±»±^/&ï@›Ø?©²»»»+s¸é.íe’°ˆa·¯ŠìÄ\%¬yffffzzzZôŒ‘PAc*=í'ÿB:N¯®®®òþ>L‡6¢d€Aì‰Þƒ†Ê?~üø±Êâ§Ú9!%•;dª÷Í0èdÀ#H0DÆSÜÃÈ¢ÏÛmYádûÅ’ ‡´,œ ã>G±\~O£TBð!'pξ {ÿ¤ ï>«“ ë\ÁqX@ó½@œªó‰*2µ©$ˆ¼hJ†—+ *϶+›<§þøøø¸é>øžü#ê@0'Î|öìÙ³Ùl6+ûœ—¤D&ð¢’!¾2` nzOqO"5DQºÃƒ!™ÉMÞ‹VáÞ½{÷DáŒa‰f sŽj4 ûýX]a¨©T*5777ù,äÔ 9‡îŸ¼ ºÏj'•J¥Ö×××½¶“L&“wïÞ½k7$“ÉdrgggG§c’ׂ9]Ântއ8ªL¿PTœ±*cŸ½¦Q4%ÃO Ôl6›½zõêUÞß;N'ˆ„˜]T¥R©Tx•nìT*•Šer@0'%$‰ÄíÛ·o˼ˆ²ÒAáE$C|eÀ@Üô ºKF§ÿøàE„ìec¦“d2™ÜßßßeB¶3,ýÙÄÕjµZcccc¢Ê####ù|>¯³];Ðxˆñç †`:ÆX©T*ñ ]ˆ²,+—ËåØT…l6›}õêÕ«ÙÙÙYçßR©TêððððøøøØ¯!1‚,ëÇFhØí‡ ôЬÿAïüž@Ëe§·¬;à I8U£odWÂLEƒÉNç-Ëß54™m¶½½½íÕ¹U U'Lñ´„‹Ê}IhV`;¢»v2™LææÍ›7y¯T*è†Ê= xdÀéA þGÑb§ó„:vVWWWƒIN§Óéãããã“““ÈÉ¿eáíÏ4GÁÉår¹B¡P}FvBÆp&ƒ„žÀ{5¾-˲êõz½T*•xo6›Í'Ož<<«T*•êõz]¥íG=’õ fˆÛO¹TH&“É<A<,ìöÃbŠÆ@:Nß¿ÿ¾ì;O ³Ùl¶×ëõdc B¥R©lmmmÉ>gwÀAœ±ÏÝ‘·,÷$ˆ~#Q …BA4¾-ËŸóEæÜét: /ÏŽ:Pç©nÞƒ–ð€ÞÝ‚.`n¬­­­A”P*•JûûûûÎ0°ýýý}YæA$0@z#:ý7}G0 …BÒÙ´”J¥$<ÌI¯×ëé æ-v:£TB’ulyÏvêóððð’ðÏÖÓš£Ô¸sçÎÞXJ&“ÉÛ·o߆8?ÜŒ-è&RÕøfä`º‡³¬·FÑ`0ðda޲Á`0ðr¨cŸï sX½^¯Cr,ôa·4²ÚõŒ­­­-§s$›ÍfŸ?þÜO"Ô­­­-û<«ÁB§¡öƒsq~lmmmÉÖ™t:ÞÛÛÛ“é†ÏÖ§}$£^¯×!{¯'ô¹íÆ7†íôŸÍ¡bè½Ù¢Á èÆÒù<Ùt„aBÛ2]"@P2˜Ö§Î¶Tû¼Š‚\«TŒ5v£ Òoþ±odÞ£x¿ÉŽê†Xèï·ãfA»è·¨ÎS¢wâö» s«é¹’wøc²M'v½…Ý>&¼ŒÜú¾îy^†Û™ƒ¼ñ«¢;7G¯Š"QTƪŸ9T¶wñóló¿L/*û2h$êþÎmð”@åî–ÛÀ:$DKD·Ûí®¬¬¬„UþƒH<2`€ôàŽ(œlï‘E ÞI¡J¹(¡zÕ5ÑjµZ‹‹‹‹aË!‚æ(uœÑ8*¿]Ýtýúõë* ¥Ø)¼^£)!'ÁQ%ì2ea·/âÖ­[·L¾{ ‘PnYêUîqg2™ÌÉÉɉ×ñ¦zLù¬é÷ûý•••/º‡\iÀð^ÃFg2@(ïY–ÚÝ-·°F•»Œ2šÍfssssSdz¼´¶H<2`€ôà/™Ì0ß#‹¢~}ùòåË:"8ºÝnëFB«Õj?þ<öÍQÁÑjµZ·nݺÅû{»Ýn_¹råJ˜5äÃ>È1Íæææf˜eÊÂn_„É9·Ñh4tUåðŠ(K½iç# cÙOŸ“%Äð^1̓§“¿UPõ›¤\.—FÃÏ3îÞ½{×Ë`Ä ’  =ü:ý¼(€T*•ZZZZÒÑF­V«ù]K Z­Vgggg±ÿ š£Äüö·¿ý­ý@6ÿÍf³yíÚµk~ÛòC³Ùl^¼xñbÜœ2LÜÛ‡P«ÕjÕjµªó™"¡úý~ÿÊ•+Wxû‹ _Õjµ´±\­V«år¹ì廲Š˜£YÂ@%‡ŠþæÐé9ît:¯¾úê+?ÏÊçóy¯·jµZ½qãÆ /ßÅ ’  =üÞé¿ßïïîîî†!¡Ž(D_W€eù[K‚¦Óét¦¦¦¦¼n¸Â‚æ(1ív»íuOÂPÙü×jµÚüüü|˜:l6›ÍO>ù䓸8†ÂŽÈ »}tâ1:NçÒ¥K—ÂüÝý~¿ñâÅ‹²Sp“Ž/?†xXm®¯¯¯‹NÿÝ®S 3~¯-ªTÛ±,›À²ôtÞV«Õúè£>Òq —Ïçóª‹X±X,ú$ô@2à‘¤‡·¤Óéô™3gθý“h! Õ`Yo×’b±XÔõ<Ý4ÆÈÈÈÈÄÄÄDTÇ'ÍQbjµZÍkô²o6›Í÷ßÿ}aɪ™ºÛív{bbbB÷‰°n·Û=þüy“á×¢ˆœ°ÛÇJ>ŸÏû}ïæƒV«Õzÿý÷߇î-L8¾tØ5*èpDCÊþñ®S +ÐêEn$“Éääää¤ìsök²ï9ÿØl6›££££^¼wÅb±¨{’bòLMMMñT¿ßïÏÏÏÏŒŒŒè Á ’  =ðï“Q(Y4•Ž:wîÜ9™ÛkµZmddddlll,ì$e­V«56666òWòù|>LytAs”˜Z­VíeœøÝˆw»Ýîììì¬J›n‹Å¢ŸýU¹\.ŒŒŒø=n4 ·>Â~çÈÈȈßßj§Z­VGFFFdú»}¬”Ëå²W}@ç6瘸v0666æeNbŽ/¿Q8ìýCÆëƒ~ôÀl)¿ŽhHÙ?º®éÎÆÆÆ†—ñrêÔ©Sãããã²Ï)é]VbÁdy?@J³¨” à’ 6=ˆ~/Ö²H„QùYÝdB e½´§9 RzÉM_aÍgÐ’e¦Ç´d•_9TJ´™(!vû˜•:ÓÝïUKRšìóÐr…вqºÚ4ÑçL–ý#Ü ë]£$ìÚÀAè…·a4Q§ž‘³–æh½í¼:ˆðqˆäH&@l/šõ#sº ?u.ýä @\˜›››%Ÿ°,Ë::::Šs¸!AÄQ6ÙƒƒƒƒgÏž= Z&B¢{o™L&³°°°´LAÄgõŠ\.—ÓyKƬì_«Õjíììì)Ó0ðÙgŸ}&ú»[”€Ê_ñúýd2™\^^^–}îÅ‹/¼¶ADpˆzaÔÅ%ô"º÷¶¼¼¼LQAè¦Ýn·····íÿ¦³ A ²++++´_Ó $ Û>¥À²,«T*•ÜBd$“ÉäÇf2™ŒèsT2Œ ¢È¡GÞäxà¶gLOOOÏÌÌÌ-Añgwwwמ¨Mw‚¨ì_8ÈòöÉh æ€8êõzýäääDfü[… DTXXXXài:ý¼(€D"‘¸}ûömŠ B7““““Îlåªõ´ bØ‘•ý£JMæ8}úôéÑÑÑQÞß#µO†f‡õÝó"‚ ”/,!/¹/q%%8ªkds™C”P4—¡0A§Óéllll„-AAaŽ\.—sÛommm¹}žDQ”&ÃÍ­r(¼C”Ptú?t€~¿ß¿råÊ•v»Ý[‚ ‚ ” š `@“®f%”åÈúæÄÂɵk×®Q ‚ ‚ ÂEˆœ™™™™éééé°åVºÝnwvvvÖËw‡ÆÐï÷û/^¼HÆ?AAa‡"D ‚P^(—Ëå©©©)^]hUŠÅbqttt”Œ‚ ‚ ˆøÓn·ÛÅb±(ú\¿ßïÏÏÏÏÓ>‘ ˆa¥À²ÞMÜ#EÅ!Ðét:SSSS컵Z­fZ^‚ ‚ µZ­6"€ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚€S¯×ë…B¡àö½l6›íõz=ö¹^¯×Ëf³Ù åšt:>>>>él?™L&u´çÔ³õz½®£-‚Љh¬¸ÍªŸ•J¥"Ã4– ‚Q( ²9¤R©T–“ ‚@ŒŸM©s!ŠúB2™Lîïïï‹tÅs˜xAælÑ…olÚíО ²}H”çJ‚ Â0“lÞ¢âöÝããããt:ûw™Dä01±èŠ¢3†AßD´!,‚Ç ŽD‚ â…Ì @û‚ ÂÕS)q]éË„q"r6ÐâNDawÈ~Ï—_~ùeœ~/AÁ":$ˆûžŒ ‚ð€ÛÂÁNT£ìß#"}˜0ÆE§†:ó „I†Ù :¡sŽaÞ&ž}AÈ9è:Añ7Ü6§n†%4J ÎF©h#obƒ.2‚â¬g"~ ³€·)çÊñ"~hÌ!ƒ7DmÞ$‚ âfÔò<Ųh€¸ŸR½1W5+ÃêàͲÓ8^ä}‚ dðæ¸ïÑ‚ bɯýë_ë64ݲ~·ÍiÜ7¦¼SÆ?o»ž ³¸õ+UƒÚÍ1EËŸâ6g¨èšç¡P^‚·9Peló¢ uî'x{ÚCAD¶ŒóýzÌüò—¿üeœÛ#†?N„a᫯¾úÊÄsM8‚ ‚PÃ#4L'êüüü<9k ‚ "ÛxÓ=P‚ üÀ;}’m@½†³AÄ ^„ŽlÆ»ÚG‡;AÄQI3šÝq.4¤‚ˆ7^ªo¨8ywÌyN '% ι#N×…T¯ÒÕ>Bdí§ƒ@Â(Ð ¨jG$€:NÑàH¿¥¾KøATe‚®¹ØùÞ&7Nù8£âLò²¶@+Äè"êëß°­%&u5êƒÎ#DDrÄ/';ô0777çöN–———MN¦Ÿ¶qÈår9?ÏÉår¹¨LÔ´è„G:Nß¿ÿ~"‘HÙn·Ûí^ºtéR§Óé8ÿ–ËårN§óÝwß}ç&W£ÑhÔjµZ0’zÃmÃP*•JaËe æÜð;o1R©Têðððƽw†q- › uÞív»++++ý~¿ïöŒï¿ÿþû;wîÜqûîæææf³Ùlú‘“ ˆh ÕàdkkkËîm€N§Óÿû¿ÿû¿™L&£S–T*•ÚÛÛÛ#'€w’ÉdryyyÙíoÓÓÓÓ3333¦Ú6ý|Œ0#Xך ,---ùu¼z¥Ýn·oܸqÃíoããããnÆ«Õj-...Î#ÌØÚÚÚ [–8Éd2''''™‡Ö’à SçÍf³¹¹¹¹éö·O?ýôSÞ6²ÙlöùóçÏi,A’L&“çÎ;¦ µZ­Öh4ÏöûýþÊÊÊJ·Ûíš–‹ÀÅÖÖÖ9äÐZ<t~ëÖ­[l&£Óét.]ºt‰æQ‚qXÖ[¯ýÇòœÉd2ùàÁƒ´HáÄyúÿäÉ“'ß|óÍ7kkkkìßΞ={ÖdýíË—/_†Žl6›å…:Ã…èÞ¨‰»Ä3333ÓÓÓÓºŸ«Êõëׯ»]pB!«ÃÍ;wî˜\s¢­%ÁƒEç¢+UNnܸq£Ýn·ƒ‹ æ°¬·N€{÷îÝsûÛÍ›7oêû'ôá<ýg†ÿÎÎÎNPQ©T*5777gêùëþ5{þQøh.—ËéÎo2999 é{Åb±8"avvvÖë‰R»Ýn_¹råŠÛ=V…¬^ÖHßµ35551¢°AkÉ;ìÉ6WqXn ¹e°é\t¥Š…ü)Aè'P€e¹Ÿ§ÓéôåË—/- ÃíôŸºu»ÝnQŸ}öÙg¦žõõõuŠ‚^ …BA5ù)ÛÀÔ†±Ùl6GGGGM8ˆø`z͉*þ–Ø“nª$Ûd9&¼80ê¼V«ÕD®|>Ÿ[F‚ ‚'p€›Çž—YÞN§Ó™šššâMfÕjµjFòá…wúÏ2 Λ½l6›={öìY•ï4†Û8ƒÞÿ#pÍf³¼ì̶¶¶¶ü–býøã?–}æÛo¿ýÖOÄpÓjµZcccc"£De¶ü0†y-aU¤t%Ý„&fÇ™N§ÓÙØØØ[‚Ð È [°ççççE¡šNΜ9sƲ 9Õí÷ûýùùùù‰‰‰ Ñ]¥r¹\&g€^ìïçààààÙ³gÏìïv»ÝÇ?fÿoÒHófoyyy:È6<ï}·ÛíÎÎÎÎŽŒŒŒ@ª¹Ù¬C‘Ýaúþíï¤X,í+•J%JŒ¦NXýœ…£;ßcPñ»!÷È ¶ŽCç®sçΣ’tïÖµÄT)u&¦Þw»ÝnOLLLèx.dÞ}úôéSè|.; „BW2;²hHÕ>ÍÐÀ„ƒ:ìw¹“ÉdrrrrRök×®]SMöÄ6am®â€Ó«½¶¶¶æ¶yÛØØØ`÷$MéqÜ쩜T«ÕªJØ^>ŸÏ«:éˆ`ÌƒÌ j7 jµZÍéè\]]]†d™QÃí´/Î=¶þúùù|>1€ÆÇÇÇO:uÊk;qbX×’0I«Î£Œ¨¬µŠx#âŠÖ+Íf³yíÚµkϲPÓS§N}¶Óétöööö¼ÊÅî@íìììx}†{¢® ²s«´ï'‰»W»Õjµx:l·Ûíííímöÿ&3ög2™Ì‚‰g‡Å… .@NF£\.—UŸßl6›ï¿ÿþûûûûûÞ$$LÒív»GGGG¼¿·Z­Öûï¿ÿ¾›twww×¾9L¥R©õõõuS²h~côz½^®åóùüÈݵõÊÚÚÚšÌø=}úôé dÂ̰®%a&’VGHÅÑž—î⸭Ý:3â´g í9ì÷ÁE|øá‡BŸùúõë×oÞ¼yãO2ËúòË/¿ô‚œÍf³½^¯'JÔ¥’Ûž–‡=¤—urQûìΚßÎ=ýgØ£R©TjiiiÉkÛ2L%L&“Éï¿ÿþ{]“äyÐÚëN§sýúõë^eév»Ý/¾øâ ‰ÓDåíƒÁ’Ï™¬I†ý~¼=A”i 9ž¡S,‹¢°é—/_¾ìõz=û¿¹]Ã-È H²¬­­­-¿›a_À!ï?‘H$=zôh0 …=Î<{öìÙÁÁÁAØrDa\K,+ÜDÒaêã: N›Ç?ÆÒÇ£[ÇONNNx9–¸Ø¯#€ö þÐîdE™B¡PxôèÑ#è=/ÖÉý&æ²·¯’!œuv¯íCOÿAF8óHè ™L&>|øðÓO?ýô»ï¾ûÎïä}´öºÊ]4"z°«TF£a¿K( ¡~óæÍ›×¯_¿¶ÿ[”re0Ç„jõ;l®£E=~ÄyO¡›a]K––––ds›SWB÷¶UsG «Î£ Äa„-ùÔÙ#K~ uà{É%”Íf³ÏŸ?]ÇS©TêùóçÏU×l“{†(èYFª¼xñâ…Îça¸ßW( ^³Ê–J¥’_'€Ÿ á^ÚW=ýgèŽøæ›o¾q«Á¬;€ëÌc™H$~œÎdD¢çA<ÑnÕˆx’Ïçó*‰äxRred³Ùì«W¯^é ÝM$‰û÷ïß§ÃE¯×ë½|ùòeØr„Ͱ®%²ˆÒF£Ñ€Î©ª‰¤‡UçQRyŒ6j¤Óéôýû÷ïCHªk6íôx@/˜'—‘N§Ó««««~žá';·×å§}û¢¦’ƒ¡Ýn·Ÿ>}ú”ý¿ß(€¿üå/±?ÏŽ.Çiü3¼:ÒétzoooϹÀðž¹ãV} DLOOOÏÌÌÌ„-Õˆ*b8$ÈÔuM0ê ãZ"ë~Œoæ 9†QçQ†’ÿ™¡R©T¼žÆCóÑžA/F*÷ûÝÂWÝ(•J¥ ’ë¹ñÏÿüÏÿ¬#³ìòòò²ƒõ7¿ùÍot´Íî ÚÞÞÞVñ„Úï2Û+>x…7ë0pxÆ?CÕ À3þyσVÁ8:::¢»h„ ˜¯ø‰h"† Hˆ5ÝÕÞµD–HZGtH¹\.»]ÅVGÓÉÿ ¹vâVª7“Éd~õ«_ýÊÏ3dåÃiÏ íè„ÈpÖÁ’ëEuðd2™ÌÍ›7oª~ïg?ûÙÏt´¤°ß§órªÙl6Ÿ>>>v›g£\ª&™L&Ï;wNö¹F£ÑpF9AÉçóyÕ:Þ> Ñ„² '1<@Œ6–CÊk9ç¸arÈéä:ˆrI:Ù!$ÒÛy]‡ö f ¼  ¨$K­V«é ËØÚÚÚÒµi†z!ù ¼œÊ™nßéÕu²b—ÛoÀÞÞÞž›q®3Û¹Šà÷¿ÿýïÉø. …B²iªV«U•M‹¢:<<<äõ©T*•:<<úè#hâdüÇ hŽUot½^¯«DQ¥R©ÔÞÞÞs@ÂP1Ù€QH÷pÍö‡dBív»}ãÆ~žÁ®‘á%˜Sëf°%ÿ“‘ËårQJ(kêš į́ ÕjµeŸk·Û퉉‰ ÝÑ^Ôëׯ_¿yóæ ä³²†‰D"¡R*Ñtû¦Nÿ™jµZMwU¸9L¬cQÀtò?QÖy†jßô{e6H 6нú›NÛ„ö f ÌÐjµZçÏŸ?¯^Ã’1èrܹsçŽãóèèèHEn{…*›vÓí›:ýgØ£üfäE8èHhÇ«€Œÿx5LTç¸{÷îÝ•L-‹EgR{ÙÊL&“¹yóæMÈ"6¢R…Œ8f('`@ÇXœN% ýäóù¼ÎD qÉÎnYæÖ1ì˜HþÇ’:3ã’|ŽeŸ‡Þ7Ÿžžžž™™™Ê7hÏ`ž@ÕjµêV s¨Ô“t#‘H$nß¾}[Õ«&óZ™Ædû&Oÿ:£œÏ²ãVBÄ/ªN€¸ÿª¥ö0f:õC2™L>xðàlÓÔét:—.]ºãdÙ—E§œö«0W¯^½úóŸÿüç¼ç¨D…M˜Ù²‡µŸcøÝ*Æ¿J–gAõµ|>Ÿ×=ÊKG)$Û‰©uL†¹Å²`÷ÈU®Ù6›Íf*•JyÝã•Ëå2¤êÌmW0W؈F¬ ‚.½½Žµ× ÞK¨8äDÝÎÑÑÑ‘,ŸÊ©ÉöMŸþ3tFðþ¤R©ÔÒÒÒ’9ƒò®TsFúÒ[–zB2Ù}ÄV«ÕºuëÖ-Ñ36666:N'‘H$x%1!*UȈ’³‚ÐÔ0!ã_­%ï`‡F:³{¯®®®:¢¢sëXT€Ü#÷[òZ•Z­VƒôÍ(D÷™‚ö æÑêpÞƒ1yb¯Õ¨:ÉGéniì ˜jn?ø‰jh·Ûí§OŸ>uû›Îd€–õvƒúðáDzœ!«åê… ß ñŽz½^—ÕHöñ!«k»¶¶¶&;yóæÍ›×¯_¿–µ¥z}ˆ ‚:·’ñ¯a[KÊårydÄô¨eyÏζÎM­cQsò?æÄ}ÆKÉp‚€r@CyÂZ¤Ù$½û5ìwkì4›Íæ“'Ož°ÿ×}ža4èt: ?Ï[[[[só¼ë|·ªÆ?CÅ ðòåË—½^¯ç]J²}†j6rY][h("´¤NØ×— 2þõCk Ñ£–õÓë‹Øunj‹ ¦“ÿùAtÅðR2œ  ^Ð$ù|>‰Û#‹ »1í7K¿Î<:­xÉuÝ›òjüÛå€8 '¹¦3ÄO1Y#YvQg("¦²FÁ ãß ´–x£Óét¦¦¦¦X…Ñgû Ì:7¹ŽEˆÞÃÌ"/sÐc¸>BÄ—X9,˲nݺu ꥒ…_µÌ¤.·ÊÉœéöíQ~ïç»áÌ3 Ã(•;¼|ùòe?Éýÿ ˆz’K¡`ÁP( [[[[²ÏU«Õª—Ü&²±©šïC¦92vÄ2þÍAk‰?ìVDŸ³_3ĪsÓëXÈf³Ù³gÏž}F%ùŸ t®÷qƒö æ‰@”%ÞŽJr°qÑ¾Ý ª3 Àd•ÞªT*•úÅ/~ñ /Ï„nPûý~ÿ«¯¾úJ¶Y€8 Î º¶bžl6›½sçÎÙçü”"9ót—´yúôéÓ(%t¢ÓŽxCÆ¿yh-ñG»Ýn߸qã†è3N£›ÎƒXÇ¢ÆäNT÷ˆC{ÄÎ`Y°Ì¬*¨zo!ƒZÅóDû:³ôÛ1Ye@t‡JµÒƒe©ÿ/^¼øõ×_=777ç× °»»» =Ô™A¼#N§ïß¿_¶ahµZ­ÅÅÅE¯íˆœy*'ö¹a†6:œ”ÑÕ¬øBÆ0ÐZâ^•!˜tÔ:†ÌÉÿìÈ÷ì{‚B¡PE~lmmmAžvÙKÚ3˜'–ݨ„™È’|Y–úI_í;#'Μ9sÆO=Ã~ËD²ž‘3>>>.[í¨ÿìw´Ûí¶_'/Ÿ]ï„ø1Ð:ä:k$»¡RÒFV"'ìÐF7 'et?9~ñ´–cYÇ0IþwpppðìÙ³gAÉäâÄö2wôgøÝï~÷;ׄT¢JBw$“ÉäÎÎΎΉú£r?•J¥ææææ Ÿ•%ù²,õ»¹AµoH¥R©¥¥¥%¨ŒnØï`é>ýg¨zëÝðjü3ü:Dù ìx-CÄH&“É»wïÞ¥ûŸï€Ö!ït:¹¹¹9¿á‚¢yG¥dßéÓ§OŽŽŽòþvh£“2IH‰ð ã?X†q-I&“Éï¿ÿþû°æ :zÃÄ(„”Û5‰=:–ǰ—ñ¦=C&“ɼzõꕟß9¶¬wà¡;,ëíÄxxxxx|||ì× ý±,õäP/ÄùàÅ«DûÎ(¿Éô–———™,¦J­´Ûíöööö¶×ïû5þírøq@&:˲¬\.—«T*Ùçœd³Ùì«W¯^ÍÎÎΪ~+:ÀÜ»wïäÝ_¹rå ¦M“(™ Ž2›&€œ”é•-•J%:Ý èÜ÷pä Ƶ$‘H$=zôHÇRvzì¶ [çQ]ÇL…äÐ< Ã^ÆwØö ló2GXìØŽÂÀ`Ž€Á`0ð¢¨ÔK²­\.—“݉Éf³Ù«W¯^•=Ë‹W/¨öuE8'a“÷‘¡‹¯Ð#2þ*Ng©B{%¥R©T¯×ëÏZ–eÕëõú£G©\‰HX¿Q*õz½td?}ÕŽ(Ì ãé¿eÁOʼn’-ëí;…”¾"Ì¢büŸ?þü0Ÿréf˜×¶‡ìõz=/'iét:½ººº*úŒÛþ)L‡±ŽaÆdò¿ú_ñ.eU*• d aÈQ6úg(•J%U;šÿãGàét:}|||,J±¿¿¿o*Ô+™L&÷÷÷÷EíC&óz½^‡$¶ »—¸R©T ßÉù »!vûnØeòêQ·¿Qÿ‘%6 •ßΰOâ¢ï{ÙHˆÆ”èyÙl6Ûëõz^Dû»€Œ}¨ö%è˜ö² CÇîD8"C‡¨¿ú«!zñ£•>®ò^íÏuö³0û94q“NØœæïVY×t`ŸWe:Wí¿2=ÚÇœ—9ܼñ>Lk‰lýw®¹ÎþÁž«¼=IÐ:·,œë˜  {ˆ\^C–å¾' bjYÁ¯%¬Ïè´½ì/ÂØ3@нΈö»ºl¿ÁàÇï U!úQ(Ãky,^x Ï‚ÔoµZ­Õ¶ƒlß^^O%ÿ#N§Ïœ9s†ý¿é»VÐÒ¢ïŸ?þ<‹|`@Oþð"dÏS9E`är¹œÛx8<<<”EÂ`þÆÓ»a¹&dYðŒ¹"ì®èÊ ´Úïî`¿ßﯬ¬¬`>MUéã콊u¶¡À|*IACkÉ;œûGg–t5 ËžnYâëUAë<Ìu +a$ÿc§µ"NNNN ûs†©œYQ„ö bXµo=~üø1Û#F Ë9àU:’]ùXÛw)ËËËË*ÿÒÒÒ›„ý8úHõy‹‹‹‹NGİ¢+\v›HNƒd2™äÝwÛÜÜÜŒBˆçõëׯ«ŒU‘ƒ[èA`ÖýÈBÇIçá…äÜrfÕjµÚ€b±X=»X,!Ï©Õj5³¿íôà¼V2t/›ä?ÿùÏÖÑv«Õjݺuë–Žg™nßn¤d2™ÌÂÂÂä{N¯tP“m»Ýn?}úô©Ÿg0'Àý×ý—ãßÏóºÝnweee%ކ¯*ÐòJQ£Ýn·oüöîFªs=øY+EŠYQ Ã6fqeÃBšüµ÷ 1¸@ìH(²uKžu‘ #í"á"W7·‰Ý+\€D’‚]+±uei‡PÀ ­ÒD°Ø®Ä`)‚¡` ´Óï¿àNîÜñùšïýý*˜™sæÌ™sÞ÷yŸ÷y/]ºÔúxšl›/¾øâ‹°ho©T*-///÷r?û¥R©T>ýôÓOãÐ?î%½•¦ýä˜Ï8ÿKCQÔŸÓfèÖÀÒ® tÚÿ·û·ëv©¹ è¼3üOÿôOÿ4èíwšÐ<ú?è‹íµk×®u{¨Õjµ_þò—¿ìÕHj'ïW.—ËgΜ9³Û/v“ܘZ____]]]m}üÊ•+W¢æ¢‹ÅbXôzuuuuÜ–P+—ËåóçÏŸö~À$s/évŠU:æÃ‘¦ø_§Ó¥Z­V?úè£F=Ca´ºS­V«ŸþùçÍíš@7Õ†{}Z]]]í4¥&jİßÛoNÁN“Ð:ú?èjä“4b\.—ˇ>Ü‹ÀÏ8›ä‹þòòòrkº^.—Ë}÷Ýwßµ|ÙØØØ›£º¸¸¸8.#ÿ­Ö×××Ož*¢ ã6ëuñ¿^Z\\\ì¶²[Lj›¡P(úq­¨V«ÕÇËBz þÁíåÅ;Þv|§¦¦¦z5B¶¾¾¾ÞÎþÕëõúÉ“'OkûX7ÛoN«?tèС#GŽ {]kq²a­EU`mœ-///OMMMu;ÂÑÉHƨh¾FLZ0 ù³ÅÝד©©©©qKùÓøüÝ^ûÅÆ=(ý²î%ŽwÚ¢h­íÆýû÷ïïEf7óaTñ¿B¡PèGÅ=«=“Úfh\+zqŽ5Ú‹½ºŽ E;ëvºf}¯÷«åR:]w»뉧ռ>eÔ{7¯‰9Èïa·Öú¿0i¯ûÎoZõz}æ¤sq–G‹³Ûî%­çÇ0Ú+»í˜Oº¸5ܛŭçÞ+½¾þ‹A·yœóù|~{{{;é³é{™NƒÔ|ò…]À²ÙlöñãÇûÛXoI saŸuØí^…ôËn Ú$瑘Àhk®Éd2­ë7Ï¿j]g}a‡ãV&`< Ê×_ýuãßÇ?ÞÈhûߺÎ$0îܹs§¹ÆJ.—Ë;wîÜ0÷ €Þ •æFA&“Éœ:uêTÁ‘#GŽ:tèPŒþ2+@´Z­V{ñâÅ‹æÇ>ùä“OdLR©ÕjµæÎ}£ApáÂ… ™L&Fÿ`œe³ÙìÌÌÌLóc¹\.7????¬} ·H­9 —ËånݺuëøñãǃÀè?ŒºFñª°"UÙl6{ïÞ½{Ikª£i}}}}*ƨ,y7î&á8 ZkÀ/~ñ‹_ý€ñ²¶¶¶ÖZ¹úõëׯuþ&Ÿmi”"~ IDAT-ÔÐ\$˜ Õjµº¹¹¹9ìý 7hK­V«=xðàAóc[[[[wîܹ3¬}úãÑ£G*•JeØûÑ ­©»û÷ïß?)Ÿ -ÚvãÆÕjµÚøÿµk×®ÕjµÚ0÷ è­R©T* …aï½#@Û*•JåöíÛ·ƒÀè?Œ‹Æx©T*Ž®T*•¦¦¦¦tþ&ÏŸ {O7nܸñÉ'Ÿ|bôÆ‹Ž=ж_ýêW¿Êf³ÙaïC·²²²²“ÂÆÆÆÆ°÷uåóùüööö¶c„IºF?~üøq7Ó„ŠÅb1éú¿²²²ÒËÏôF6›Í>~üøqÔo·X,›_?;;;ûòåË—a¯ÝÞÞÞÎçóùa} MÃO#°’nÜnÆ@¿A|-ÚÙùyG>€Ñ4’Ëæóùüõëׯ·ó7KKKK‚½sëÖ­[ssssaÏÕëõú™3gΔËåò ÷ Ø]jµZíèÑ£Gã–-»råÊ•ÙÙÙÙAîÀ8¹ÀììììW_}õU&“É´>W¯×ë¿úÕ¯~µµµµö·KKKKF‚º·²²²²°°°ö\µZ­>|ø°Î?0H…B¡Èår¹o¾ùæ«’Œ‘¸t±—/_¾láÙØØØ&Ö{qóþ[¿`wÄ€v¶) F‡)$ŠêÔGš‹jê¨v&îæÛ†<0Þ†HÚ®,0 ÄŠjÐ%èDXë°¶/*ãXa†‚èB±À0ˆÕK;’uÓ°D]zQ yÇpô¥©’>ª¢¨ S·¾¤UDœ×“!ê{Ç eØo¡ó?ê:0ŽÇz¥WmB÷éû‡ø‡èÇûþÅ_üÅ_ìÊÚÿ÷ÿþßÿö>Ф ܨ]`úµ¿q£HͤŠO®q½ö‡5.Òïºù[˜D½nºO#'©S=*EÒâŠMîìtÞùO“ Ñ‹‹®ÔVú!êK»÷‚æÏö·£üî~¿£§Ÿí­~ë¦]5ìLÇntÛžìe0 éüIó;ì6ë3éx„}׃¸‡ò>ÝÎ9‘öÚøNÚÓ[ù|>üøñãaÏÕëõú‹/^Äýý… .d2™LØs=zT©T*=ØÍž[YYYyöìÙ³\.—ëäïçæææ~úé§ŸÆ¥;Lëëëë‹‹‹‹aÏÍÍÍÍݺuëÖ ÷§X,—–––ž+•J¥B¡Pèô½¿øâ‹/ææææâÞw}}}}uuuµõ5™L&sõêÕ«c9eW¨Õjµ‹/^¬×ëõÖçþó?ÿó?¯_¿~=ìooÞ¼y³\.—û¿—Ý›ŸŸŸoç^Éd2÷ïß¿¿³³³vå{ Z6›Í~óÍ7ßtÚÞê•Ö«A)¯\¹reœDÍä¨vRZ QãÝbmÂAܧA†vΉ\.—{öìÙ³¤ï_`²ÙlöêÕ«W£:ðOŸ>}úäÉ“'Q<‚ øñÇìÁnöÜÆÆÆF·6ÚXXXXT­ˆÙÙÙÙ+W®\ {®ÛÎT`akkkë³Ï>û¬ù±åååå° þ0"§\.—oÞ¼y3ì¹_üâ¿»lmmm]¾|ùrÿ÷®7>øàƒZ[\\\œšššÚ»wïÞ­­­­vÞï믿þºw{ãíôéÓ§Ã:A»A.—Ë;wîܰ÷#|>Ÿÿî»ï¾ëu fmmmm7Öd›°Ÿ÷él6›½wïÞ½NÃkkkkqA€!Hº(¿xñâE­V«E=7ú_¯×ëwïÞ½Û‹ý쥕•••…………aïÇn8{öìÙ~GâãF!ºíüGªÕjõ£>ú(ìw´¼¼¼\*•J­/,,,ìæˆ9£çòåË—Óv‚ãÎùQõÞ{ï½×üÿR©TZ___‚·£+G=uíjµµµµuçÎ;ýØOGüñÇÃÞ‡a:qâĉݞٷ´´´´›Ú5Ãhöë>•ÅÐŽëׯ_jã X6›Í^¸páBÜkâF1’Fÿ····Ÿ?þ¼‹]ì¹ÙÙÙÙO>ùä“aïÇnÈd2™¯¾úê«~¦ÊݺuëVR*V§¾üòË/[ Õjµ:???— \( aü´iƒÎÉT„F'¦¡R©Töïß¿?ìµÓÓÓÓã’²Í`ÕjµÚG}ôQµZ­&½öÒ¥K—Æ)ý=›Ífgfffš Ë^[___?xðàÁ¸c0ŽÁè§°ßW˜jµZ=xðàÁ°{ÓÑ£Gó7µ¼¼¼u »·:tèС#GŽľ޲q›Ña´ ûqŸÎçóù³gÏžz¾‘)—”-—Éd2Q}N€KýOšÿ7ú£9²ÝyžôGT —Ëå¾ùæ›oú)ÊüèEç¿X,[ß»^¯×?ýôÓOÓü>ûì³ÏZ/š¹\.÷å—_~ÙÍ~A/U*•Ê¥K—.ަyä|œ}ÿý÷߇=Þ knФý›ÿŸ4ú£9ÿ?MZ\:m (ÉÖ×××ÃŽñßüÍßüM?¶÷ûßÿþ÷aÛë¶óAðçþçÞÍhzÔ(þÒÒÒÒnOd´Dýn{ù{´°ß_Rã¨\.—§§§§Çý³C¿8pàÀôôôtÜkƽfÆ7n$º†Õ'_Øu?)3ªYët«IT( ýh¦Í„éÕ}zvvvöرcÇž‹+øùçŸv>d2™Ì©S§Nµ>¾kq¸Ùµk×®…}ñiFµ[ç$¦)È7ÿ?iôçÿ§ICKŠB7ÿ¨ÂF€èÞÿüÏÿüO?Rýþë¿þë¿zýž ÿüÏÿüÏýxß~ „™™™™¸öbµZ­nnnnrŸzíÍ›7o^½zõjØûÑï¸l#3*)"‚àØ±cÇvË4€qÕ¿¬V«Õ7n܈ú»J¥R¹}ûöí°çÂêa -4‡vj*]ฑㆰÎešèh\ºFš¨bsð ÍèDGdÓŒþ‡ÍÿöqNR¯×ë×®]»–öõår¹œËårQÄQþ¼ÍsvºeÔ‹~Öïh”¿@<¿_©V«Õ’–Ì7vb;SºÒdB0>¢ú—¯^½zõæÍ›7q{÷îÝ»aƒ¤ûöíÛ·gÏž=ÍíÚ €S§NJMê˜f³Ùì‰'NĽ'£ÿqóÿ“Fÿƒ`4çÿ'Å¢…“*MgbŒâ´×N”J¥R»ÿ†J¥RyôèÑ£¸×LOOO8pà@'ïUÀX ®?¢¦k$­AðüùóçÛÛÛÛ­‡}ÿ»64æáÇ£F™ÓWi:&c ª3œ¶šë(JŠÐær¹Üüüüü ÷ `\‹ÅâNJýZîivvvöåË—/ã¶½½½½Ý¯%E³ÙlöñãÇöûòåË—½HmÖq…ï·aÇ™Ýaeee¥qîÜ¿ÿ~óV&“ÉÜ¿ÿþ ÎëA‰ûýììì쬬¬¬„ý]¹\.ÿíßþíßv³Ý¤>‚Á¶ñ×ë`×® $ÍÿOJKOšÿo=â?•tÒÆ­SI¼4 óÇ?NSÔ®ù†ÔµÝÁl·Y6›Íþçþçöò·²ò½z¿q5 ßo³æNáÚÚÚZÚϱ¶¶¶ÖÎvâ4“gÏž=KªyÓܨ ´{œ¯ýúõë¨ º\.—{öìÙ³N:¨Ã:ÎýÞî¨gv‡æóziii)íß5Ÿ×ÃäóùüööövÜogccc£ùoŸ;î÷A°´´´´³Óû`ZšAÉIÉø wve iþÜè$Wµ*¸[EÍIih4‡}á‡Q•Íf³÷îÝ»÷‹_üâ_}õÕW½h<¬¬¬¬,ýßÞhh4>ÛéÆY[[[K¸hhŒb¥éôGi\Ó»É Èçóùï¾ûî»´ûËårß}÷Ýwi¶7¬ã< ßoØ>õë8³;ôò¼^[[[—ûQãZÙîçîõÒËçÎ;—ôû}ðàÁýšuÈf³Ùý×ý×vOÞÙÙÙÙv¢‚ý7ÿ?iô?)ÕÆèÿÏ•ËåòÇ&½®˜4Îcd!—Ëå6777» 4:ÿÿS£kR‹Åbkªì åóùüO?ýôSR½š´2™L¦“€ÕììììW_}õU»Ç"Íö†uœGáûmÕÏãÿ×ý×ÝîßýË¿üË¿ŒÊgW£v^Bë}yXòù|þìÙ³gã^“T=>NR6‘Œ ÁéõRŽmfgggøá‡Ž=z´¿k7ÊÜOq1iôÿÈ‘#G:t(êy£ÿá>ûì³Ï’*ÿAÌÍÍͽ~ýúµ´dxëÖ­[·ZÝŒ ´vþ†'ŸÏç¯_¿~}˜û0JÔ••••NÛ ¹\.÷å—_~öܰŽó(|¿aúuœ‡ammm­Ývþ}ûöÉfèÜÆÆÆÆÂ°÷cÂî˃–6€wéÒ¥K õ¨iÓ3333Im¾¨ ÷°m:íÄRÃ"nþš%éâÖV5ú­V«Õ>üðÃÓ‚às¥ØÍâ–ÿœ›››»wïÞ½Æ !ª˜Xó|Åb±XŒËÂJšÞDïe³ÙìÕ«W¯{äT:¨ssss¿üå/ÙÍ{?~üxk§nXÇy¾ß0ý:Îô´´´Ô:?;‰)ˆÙÍÿ¿üË¿üˤe¹ûmvvvvsss3©?·œ9£éûï¿ÿ>ìñ°¥üZEe¸‡Õ€Hè´¿²²²Ò«ùn½7ÿ?iô?âÈFÿãÕjµÚÅ‹/ÆÕhÕ¸9³•ËåòáÇG­ñÛȘ‰+&¶°°°ÐÄ]‹WWWW …B¡WûN:i–ˆí§NÓÀGY&“Éœ:uêTócÃ:ÎÃþ~û)ì8ÛÂÂÂB'S ;É Ø­ŠÅbq\:ÿ½N›‚·±a^/Ûéü»§ŸÍÍÍͰ6_.—Ë;wî\ÔßÍÎÎÎ~òÉ'Ÿ„=V"U “N|£8Æ(ÌùoI3ú7ÿßè¼F‘˜N3AÖÖÖÖú¹ÌÔ ´Vpî„ùV»O¥R©ÌÏÏÏGzauuuuyyy¹_ïO´´YÕjµzðàÁƒQë1ŸžØØØØh·ߨ0ŠQï¨izzz:iô¿V«ÕŽ=z´'ðÔÔÔÔþýû÷‡ÍÓ‰Ûn³Qù±·ZYYYéÅFªÞ¤Ü !­~tþ‡'ÍúÍAðö;Šº?4”ËåòôôôôÔÔÔÔÁƒ¦9WâF Z5:þq÷™B¡P˜šêMc·T*•Zï«{aš@ñcÇŽ5‚¥Ã:ÎÃþ~Óèåq%¦öwšA0jú˜I0LsNÁÛÆ½<§›¥™7Ý­ÅÅÅÅ©©©©¸ûèúúúz·Ÿ3ŸÏç¿ýöÛo“ÚÒ½ìü'­šF\»víZXÀ7êº7%çéÓ§OŸvóÏf³Ù'NœHzR©Tê4HT( ¤øÞ¾}ûvÜÈá“'Ož<}úôiÜ{LOOO8pà@Úmöë8êvƒ`8ǹž?þ|{{{;êù¨Ôþ¯¿þú먿çâ€iêNDU3???Ÿ0ÜÚÚÚº|ùòåvß»aýÒ¾þÅ‹/â‚Tý¬SqþüùóI™ÃQ~ýë_ÿ:íï6›Íf¿ùæ›o’Ž}?²ùâ ŸAg$ÒI»zZœ›7oÞŒ:G(‹ÅgÏž=‹;ÑÂF’ê4:{“6Wƒhq£Kõz½þé§Ÿ~ÚÜøhDFÓÎuµµˆ¡ß²ÙlöÂ… ZoŒD´^—¿©°NØÜÜÜÜéÓ§O÷sé^¿F°’–´ ‚tµq’¬®®®¶³UÔ|ÅfµZ­öâÅ‹ÝìW«~ŽŽâv‡uœû!ͨjXjÿúúúzÒÀøL»¢G'ƒqiêY ºvRð'Þ€zÈduý/¾øâ‹¤€N?:ÿQíf’H§ÝÕÓZ%è´ŸT'`kkkëý÷ßß:”»KÜ´¸sÓ¤H%UÅ„IÕiK‰¸|ùòå°›ˆeÿ†ëÍ›7o^½zõ*î5ý‰Œ*ˆÛ,ÍÊ8½¶^q·†uœ‡ùý&éÇq¶¤Œ“¹¹¹¹~øá‡æAƒ4S«Æ¡8àììììË—/_¦©·T­V«››››í¾ÔÚݼo·â ¦5ëu!é¸ì‘^J3í¢›,­8I‡4ADºÓ¨ÇÒN&]Ô€P«ÿ tÒ‰OS'`uuu5©@“'iZš¨a¡P(¤™vâĉã^°ÒKÇK“Ê5u`’ˆÖÎkcÎ_¯:"iŠ„ ª‘Û,mJ|Ò=$“Éd÷ aça~¿IúqœGÁòòòr\aØôÁ4ìQ*¸±±±ÑºBPRæn³K—.]jw@.M1¸G=Æ@ßçŸþyšé£`[·«+ 2Б4í"©ŽG§ÒÔïF€x·jËŒë5VsISÔ>‚àN;ñiê(0E”´j×××ד¦ìÛ·oßž={öônï`4…-í”v^ØÚ²~;Ã×n'{aaa¡ÛeAÓ «×ëõqHÿNkÇy˜ÛÝÍÊårùðáÇ£:…QYËËËËq ì° ‚q³¸¸¸ØIêzÒ\ð ^:ø –Êm6ÈÂwIË9ö#èR,‹IK¿÷+ð@¼FÝNV£kõNR±¨N|ÔzƒA`¾?É#Ç?ž6«\.—oÞ¼y3êùQ(B”Fc©˜n¤YV‡ÝżñvçÎ;ÎñËår¹gÏž=kt{9zŸÏoooo·ÖªèFؽ"2ÐNç¬Ù$Ì‘¢7*•JåÓO?ý4iä#©:kRÔažA [ó8Ír~QËùXÃwt¤Y’¬] :‰jXÇÙ÷;xNaR1³Vij\£a­8ÑoÙl6{ïÞ½{“ö}%éUfÇÊÊÊJÒo¤QY^çr¼“4?-j Ô¸ª›˜7¦ÖÔÆl6›ýæ›o¾‰ª7Ñ‹uªa\~ýúõëq#;·nݺÖ8F•w¢5æ±&>mÇÂÂÂÂ8­c>Ã:ξßÁIÓ‰«†_,‹qõ«ÕjõÆ7z½¿í( …Öú@í—.\¸paV3蕤åêš…ÕbÚ͵•ÒTû¯×ëõ3gΜ‘ö?YÞiÌ‘Š[s}iii©uù“4s¤¢‚ì.ëëëëi–ó ‚?¶···ûÛßþö§Ÿ~ú)îÂ~óæÍ›.JìQËù5h­×ÛÆÚÐa«¼”J¥’hþh*—Ëåéééé¤eÒ:{öìÙæQšy²»a…ˆ~çQÛîn‘Ïçóß}÷Ýwqa£™IiУ\ãªàÒ¡C‡9räÈ ö­Ÿ’2DVWWW»)‚8‰òù|þúõë×ã^£ó?¹þo @Òšëa©ýØ}ÚMÌd2™üÇüǸegJ¥RÉ2“ì&³³³³W®\¹õüÒÒÒR󼯸‘,#‡ã¡uÙŸNF3™LæÔ©S§Úù›qY]¥W†uœ‡µÝIµ²²²rÿþýûQm‡jµZ=|øðáæMš©¥R©Ôî2[Ã&벓ó%M!æ4+ôÚLOOOǽF[ñç²ÙlöêÕ«W“–vTðorýI €¤5×£Rû …B!®ÊªºÁ#Ôq£´ …B¡ûã`vvvvsss3ªC߉¥¥¥%A€ñÒ}út§Û(þA§ߪיƒÔ(N•²½½½Ý¯Á°N3Šòù|~{{{{T®ûDK Aòüþ0 ‚¨ÆfÒ‰½³³³óúõë×IE*µâÞ§umùÝdÜŽs¡P(¤™ËårçÎ;7[iG†®\¹r%j_‹Åb1nî¤ío»ÿýßÿýßõWõW­ÁÔÖÎ;¢‚çÏŸ?ÿ»ßýîwA0¼ßѰ¶;¬ï·aaaa¡›kJš‚Na)i¦ÁÛQüv:±F`§Ç£_†uœ‡µÝA•ûo§#øãVìoÐ*•JåöíÛ·“^·¶¶¶Ön'¾qîÄ-ÃHo¤-{õêÕ«i³9×ü¸8-©An~˜N‚ìni§‘¨züVšw.—Ëmnnn¶6˜’Ö=¶Ýáo·V«Õ>üðÃA€n:ÿ ­A€ÅÅÅÅÝ^XsXßoëû7w|’FÞs‡ÓlûÕ«W¯Þ¼yó¦ù±r¹\~øðáÃ4ûÖèÄÆVVVVvvvvF½8èã<ìíî&Žàw2U`7JS"ÞÒ,^,‹iCôNšûÝÜÜÜÜëׯ_GsškWŒú5ŸŸK‚Îç÷w<`÷ªT*•O?ýôÓ¸sæÐ¡C‡Ž9rdû5ŠÒŽâ…ªt“¦e»ƒÙnü1ðɽ IDATÿñÿÑmç¿ayyy¹T*•tþßæ÷eiii)n4®‚y«<ë}þù矷sOoÂŒkÚç Žó(mwRu2‚ߨôï˜N­V«]¼xñbške£ÙÚ9Ìd2™û÷ïßßÙÙÙ1â?iïwAð6˜Óíõ‰ÑÓV þ8¿ÿñãÇÛù;ÅiW;£S»Yšb^¶;¾Ûm¨ÕjµÓ§OŸîeaªB¡PÐð}kØßo?U«Õê7n„=—&ØJ:qÇy·;J:ÁõêÕ«v*ýóV¹\.Ÿ?þü°÷ƒÎi_Óv Þ6DÿîïþîïÚ6W*•J»Ù½>J§V«Õº]¯µZ­Vÿýßÿýßmwô¶Ë`Lò÷{ûöíÛq# úÞH:Γ¶ÝQQ.—Ë¿ÿýïßîßýýßÿýßïæãÖõõõuíùñvíÚµk¿»WGhG>ŸÏW«Õj»ÕJÓ>â­;wîÜéfÕK—.]úßÿýßÿµÝÑÜ.ƒ1‰ßo©T*¥5U¯;ió¤l—ÁëÇÒ~ÝPãk¼•Ëåò™3gθæïN Dóœ¯äjÈIsÃF¡êñ¨¨Õjµ>úè£N¦Ø¬®®®všn»ƒÙ.ƒ1ißo»s¢ËårùÝwß}·—Kø^ºtéÒ¤²«z¼ªõ£avvvöرcÇ’^wíÚµka™³óóóóIs©·¶¶¶îܹs§›ýì‡õõõõƒÖôÞ÷Þ{ï½alwRôcz¶ºãA€¡HZæ'ÍòQªÿ©J¥RÙ¿ÿþR©TJû7½¨|l»ƒÙ.ƒ1èïwyyyyjjjªí%ÙÚÚÚÚ»wïÞNö©±„o·úÅÅÅÅ©©©©Qië8Ú÷Kï8pàÀôôôtÜkâ:ð|ðÁIÛˆ tjß¾}ûöìÙ³§ïÕ¸fö"ƒHQÚÁk|Ýfs”J¥ÒÔÔÔÔôôô´ºÀÿ­Wí¸IK(ívaÇm{{{»Ýé¶;zÛe0†õý6/±”FÜò|ƒÚ—v×þÃ:ΣôýÒÆò—Qâ®Ùl6ûøñãÇqŸf9½Våõ:Ù§^h§Ý×i[.ͱÓNìLÒ9=ˆssýtrC {­ÃI½·t $LsÞ¤ uXKÓîTÀ®u_zÕqLs솤×QG€á y›ÄÎqXC‡‚fIФöJÒH}§Áµ4ãAîúH3J=ŽYI¡—€¤âô_kÇeR³­ ÷ š%µo’cIÙv`Ó¤Çê|îu íg›ÔkÀØH{ÁŽcä`4„0Nb@XϽˆ†¸Qè¤hR»¨Ûú¤àB’^etH3Â?ˆý‡Id¢QYzêÒV˜nT;žšššRñ`4„U>Ÿ™™™™´÷ÌÌÌL&“É4?fé1âÎ…‡>Œ«†~äÈ‘#‡:õ|·•ÿ¿þúë¯;ýÛIðàÁƒ½\9&Rcd2j-*íyÜÒãY Q™ Qó³,ñóìÓŒrÇÍÿïÅ9Ömæå8gŒÛµíφ½Àè™›››{ýúõëaïG¿-,,,ìììì¤}ý‹/^Y$, ¦!iô?‚àã?þ8ê¹nGÿƒàmæåG}ôÑæææf.—Ëuó^ãæÒ¥K—*•JeØû£Ê k=z´Ý?þøãÃÞ†ïÔ©S§Z§‡AÔëõúµk×®Åým6›ÍÎÌÌÌ„=·µµµuçÎ;½ØÇJ¥RÙ¿ÿþR©TêÅûºÆ´Ñõõõõaï Œ…NR‡Ç±âvš¥Ò¤3iÒ—)~“Ð…Nã¸Þv»€q r@¢¤5Îǵãß*®›Ž?c.j¹®aïÐ¥4ë‘ À[YYYIêø7{üøñãl6›ö~)E¥üË€ 1;;;ûòåË—vvvvŠÅbqØŸ˜`iæ*„dQ£ÿ­)þQSš_—Íf³?~÷»|ùòåËÙÙÙÙá}âpqû½½½½ÏçóÃÞOØuèFkg¶ùìaá••••~ïs¿„u|£~;aÁ‚°ŽqÒ”‚Q«0nû Àh{gÐ kÐ&uRZSx±[:tèБ#GŽ$½.›ÍfOœ8q"îù¸ðÖtù¸ôûAüþ²Ùlvfff¦ù±z½^¿víÚµ°×ß¹sçÎÖÖÖVóc™L&Óúëëëë‹‹‹‹QÛ›››ûâ‹/¾èfß{%ŸÏç¯_¿~=êù­­­­?üðÃZ­Vä~Án•TT@€q0ÐÀÆÆÆÆÚÚÚZëãKKKKaFOšTêvøÎÿÔÝ»wïÖëõzãÿ™L&sõêÕ«IÊ[·nÝš›››k~¬^¯×ïÞ½{·_ûÚO{öìÙ³oß¾}Ímooo?þüyØëkµZíÅ‹/Ò¼wR`iii©çeÜè}RGavvvö«¯¾ú*“ÉdžêüÇuPÚÉùä“4¯]XXXhA kHGulš; Ïž={–4Èår¹gÏž=ÛÙÞHScŸ_¿~ýºu7J£CØÎ~7¢1÷F`¤ÝïnRU*•ÊíÛ·o‡=·¶¶¶ÖÚ‘Ž;V·oß¾]©T*ýÜßæ`MXvO”Æg‰:×ÂFô3™L&ª³rúôéÓaqYqA€´™QÎ;w.ê{yøðáÃr¹\ŽúÛ¸`i©T* …BÔßÖjµZÔ4‰ ‚+W®\• 'Ãö»ixõêÕ«7oÞ¼ô>MФ€hãÞ4­ Ý×@’ÚÁÏd2™û÷ïßêàŸÿöÛo¿šKgmmmM`ðjµZí£>ú¨Z­V;ùûjµZýè£>§Æ{ؼûÍÍÍͰcМ³³³³Õa¾víÚµ4Ç .°°°°ÐÎÈZ6›Í^½zõjÔï-nô?nÚ@ÚÎ$gœ={ö¬"ªÄþA|ÿý÷ßrè sûH2À{ï½÷^óÿK¥Ri}}}=Þ6V=z4n>n³­­­­;wîÜéÇ~ÁÛÎÅéÓ§O÷ëý“”J¥ÒT„“'Ožl.×,¬Z|Rg$a¦ 7ŽE©T* cûÃT©T*û÷ïßßîg/•J¥ýû÷ïïwêœÕÕÕÕ¨s8îó´žk•J¥réÒ¥KìCó5&¸ @;æ¸NUÜè6›Í~óÍ7ß„eJµÓùoˆËˆ›JÁî4úoþ?б°htÔˆZRñ·¸èuØü¸¸Ñë¨Bx;;ÝÃKS帹Ða±X,¶S;.ºßz\ãæ ¶³ì[;ûשÆ|ɰï,ê»ÚMÙ IóI‡u,çM;Ûû„½OÒÜ×VÝü~£Žs𑳤BžqûuŽwóÛëæœIºŽõjº”yÍÑtÜûuÍßM« «@Ú ™»×@—lˆJ-lŒz†t§ÙÜÚÚÚÚ»wïÞ©©©©¸ÀB¡PˆQO»öz˜r¹\žžžžŽšššjÍ[___ogt¯V«ÕvìØ±~7Ö××ד¾³Ý¬q|¢ ë¸5Λv¶_.—Ë>|ö\Øt¡åååå¸ì—fqòÓˆÊÈår¹ÍÍÍ͸ßARJuÔ”„°¢Œü7‹šBÑpáÂ… ã6݉î¥)Êûã?þ8¨ý«ï€FŠ;•°NtRC¸^¯×OžkMÎår¹ùùùù^ì?At#*8ظ&D¥é7¦‹tÓanˆ ¶Ä »I©. …¤@a'âV”‚áOwb8âV¨‚·÷‰»wïÞä>ƒ3” €~øÍo~󛸥µ¢DuDÂF"‡-®šÿÍ›7o6þ™™™™Ö¹ÿIËAt†Á(ÆSÔdÒ²}AÝ9ïEÇ¿I£ÿƒX’1ÌÝ»wïÆeN|üñÇr®4£ÿQËìö©S§N%Õ¤i·~О‰ ´J;×0ª‚xkáÂA‰›—wÿþýûa§ÅÅÅÅååååæÇÂ:ìiÓ:ÃFa‡u<˜<_~ùå—a#iT£(iô˜#ªOž777wïÞ½{ÍA€°F&“É\½zõjÜÒ_·nݺÕZÌ,®‚9$YYYY ëüAœ?þü87úççççã®=ÃLÿoHšŠÒqhFÿ`p~(‹Åׯ_¿Ž«hÝ,“Édîß¿¿W£Ú튱‚?. 6)sä××××÷îÝ»·u™¾ xøâ‹/¾hü?ªÑ?777÷úõë×Åb±ØüxÜŠ~…b²mlllD¥ýŸŸÏ÷Ýwß…ý®¶¶¶¶Þÿý÷'% 4333wÍL„”Z­VK Fœ8qâD\}I²´´´4¬ikiÄÕÍGQ™vFÿÛuÇ-“€áz'‚àôéÓ§{•b8*ëJ'¥´®¬¬¬„ÍM{öìÙ³oß¾}i__.—Ëï¾ûî»QÓZU«ÕêÁƒêüÓŽ••••¨ËÕÕÕÕ£G…ùð½’4ÿÿÕ«W¯Þ¼yófPû“$i)Ï}ûöíÛ³gÏžAíϰ-,,,„­ 2lQu3ÆU\¦Ý$þ¯¯¯¯GžšššZ^^^nçýÒL9€4Þ ‚ÞW€dA©çÏŸ?ßÞÞÞ{nmmm­u¾çììììË—/_vÒÀ*—Ëåééé鸛úÉ“'O¦m/‹ÅN _~ùå—a#¬q#ŽÍ« ”J¥RØk¶¶¶¶öîÝ»wÿþýû'e”–ÁH*ö×ncwÔ¥iŒJÀ†¤B€­Sˆvƒ¹¹¹¹Ÿ~úé§QÈZ‹«›Ñ iî_ý(š[©T*û÷ïß¶­B¡Pˆû[EúÒ™™™™µ@£ë~̃dA©J¥RyôèÑ£¨ç[‹Ý={öìÙ(¥ý7ªòïììì$-©W©?‚àöíÛ·ÓtÜ …B!¬16i#´ô_# vNNÒ|ÿVi²p’FÜíÅ‹/â‚“™L&³GG¡8`\Ý ‚økÎnËÞ ;ïÄUçíÆ J]»víZ7…îFE£!5ÊWªZ­VoܸqcÐûÜ+q#=QÖàNCR …Þ‰ÊF ‚·ÀgÏž=K3‚7‰#yI#-Õ0É…ïÞ½{7î>1¬â€qu3Fe5 `ü½“ü’ÑW.—ËçÏŸ?ßîß­þA?öiªÕju~~~^Ú> F¿§ôOšZ(ƒ.˜Té¿1…F=–É•(l<wÍÙÓwèÜD‚àmÁÅÅÅÅ´¯_\\\œ„yÉ««««æìÃ`%­0JK6¤Y pÒ«‰7j¡DÕA ‚ÁLªô?ÉShø£¸©9£x`üML þXu7.°¸¸¸Ø\èhyyy¹“âD½Ü߸Â|aÅú:©$¼Û4 kµ“†§1O¸_ï ô_¡P(ÄÝ'ææææ~øá‡úUÌ6©Ò©T* ìÒ,.è¸[ëwЙ?kÌ ÍêÄ0ç¿®ÿÁ°¶ß‰AFÙò {?hÏn?o_ëëëë/^¼xñí·ß~vÿËår¹ï¾ûî»3gΜéÕ(üììììæææfÜ|ÿÕÕÕU×ÂÁ…ûN\¬yeŸ¤ú“\¿€Þz'©Š~'ªÕjusss³—ï ½”T “Éd®^½zµWÓÎ;w.ªó?©Kf2XÍKþ¶’U@üa @¯+ §]ކ)M]€~3ß÷Š«ÍñêÕ«WoÞ¼y3è}`²½ApçÎ;q•‘Û1Œåèòù|~{{{»Wó±wvvvY Ø}jµZM¡?é x{ºxñâŸ5’ÓºtéÒ%£ÿŒƒFAÏ…………aíC£Þ@>ŸÏk=/^¼xQ«ÕjÃÞ&Ëÿ­P.—ËçÏŸ?ßÍ›­®®®Ž[ñ=€vÅ-Ý£Y•{Ïž={öíÛ·/î5?þøãƒÚŸQÏçó?ýôÓOssssaÏ×ëõúÅ‹/öªvùòåËqõîß¿eee¥ÛšQ«ô´÷º Q¿»Ýö{`0þdÀõõõõ¸¥‘â¨\ ìÊÝÃÞ:W,‹÷ïß¿µN?æå§©7°´´´d ÚîèЦ Þi}`}}}}ïÞ½{ÓÖP¹àçFmY®˜žžžŽ{Í0—o¤µµµµ¨ç·¶¶¶Þÿý÷û5­P(VWWW£ž_XXXxùòåËÙÙÙÙ~lؽþ,ìÁÆ(E¼m(…ÍÜÚÚÚúðÃ?…ùiår¹œÔ°ewj>—Çá}ÊÝq빿÷Þ{ï rŸ’ÌÌÌÌDvÁÛ`î¤#Ëf³Ù{÷îÝ‹Jù‚ (•J¥B¡Pè÷¾,///ÿý÷ßG"uΜ9sÆê@¯ü, U¡P(„¥¦=zôè(tþ-Måî™™™™^­ß I ÛÛÛÛÏŸ?>¨ý´¤ùþAðv*Û :ÿ ëëëë'Ož<UO¢Q X,µOŒ·FQ˰Õd•)üÜ×_ýuÜóûöíÛ·gÏž=ƒÚŸ$I “¾æø©S§NÅe@,...¶3•-íò³?~*—ËåÇ®V«Õ¨×|üñǧݯ~Kû¹‡´XYYYI³Ìo£Ðb»¯€I Ф•¦§§§8p`û%›Íf“V%xðàÁƒÝ˜ÕU­V«<8Ìl*•Jåý÷ß?mí€N tàÉ“'Ož>}ú4êùL&“9uêÔ©AîS”4Kî–€Íú]ì¯iV`ò$e!È> ×:¦À¨œŸŸŸ+XX­V«››››ƒÜ§a+•J¥Q¬e“´B@7:”TàØ±cÇF¡èVRÀG=…QðAt±¿v-////...{?€É#СÍÍÍ͸âm£P ›ÍfOœ8q"î5IŒIÒn±¿aIZ! ªT*•G=Šz~ê9räÈ¡C‡E=¿[ÒÿëõzýäÉ“'‡Yì¯]år¹üî»ï¾«8 Ð+]¸víÚµ¸QÚ'Nœˆ[®ß’–¿»}ûöíÝþÿ›ßüæ7år¹<ìýhW­V«ýú׿þõ°÷˜ ]HZ àСC‡Ž9rdûÔ”þ_¯×ëwïÞ½;È}š3333q•I•æs×ëõzRL`8ºP«Õj×®]»õü0§$¥ÿ?|øðá8ŽŠ‚¤ÂŠAð¶#|ñâÅ‹£¶Ò@7Ò|nçŒ.€.ݹsçNÜ<íO>ùä“a¬páÂ… q£µ»©ø_/¥)¬“×Nó¹ëõz=. 0Qòù|~{{{{'ÆÆÆÆÆ°÷è­b±XŒûÝ‹Åâ ÷gvvvöåË—/£öçñãÇ£j$]Çâþ¶++++qÇleee%é=6666ú±oq’ŽíÎÎÎÎöööv>ŸÏ÷kZÝË—/_ö;È”æs»¿ W6›Í>~üøqØwÓ|ޤ}“G@íÖy¡°Û%e\¸pá ‹ÎÏÏÏçr¹\Ôó×®]»6 ©é…B¡P*•JÿÏÍÍÍݺuëV?·™tlƒ nÞ¼y³_£ÿÅb±¸´´´ÔøµZ­ÎÏÏÏ÷»˜cÒç6úì:I#Z;;éFµ€ñ—Ðïáfq£{iFiÓd2¥ùœIû‘$íµ2ldºŸ×Ù¤ë|?GPþ›Ae—$}n£ÿÃ'€$2z(í¼Ðï¿ÿþûAì0XqY™L&sáÂ… ƒØÓ§OŸž››› {nGi+•JåöíÛ·›ëWÝ…4×ùK—.]ê×h|k]‡­­­­;wîÜéǶš¥YQbÒÎ+˜D=ôÅ_|Õèn°<L®Z­V»xñâÅz½^{þøñãÇûÍf³q†~¦¦ÓÝ»wï6÷\.—;wîܹ^ogÏž={öíÛ·/êù~vÈóù|þøñãÇ›{ðàÁƒALåHúÜ“Vð&•@4RéšçdFÙÞÞÞ~þüùóAì0xår¹|óæÍ›aÏ " nô¿Z­Voܸq£ŸÛ–°ú+ï½÷Þ{½ÞÎ0k+ ê3†‰ûÜFÿ`|$h­0æõëׯ“Fþ=zô¨ß…š€áº|ùò娩ýÌHýïgjz?5æøGÍ1/‹ÅµµµµÖÇgfffz]xñã?þ8ê¹R©TZ___ïåöš}ðÁ´>¶°°°v\é^ÕBˆûÜFÿ`| ˜u·aòÅMÈd2™«W¯^íÇŠqÓúÝ9„…………° lXç?‚àÅ‹/z9ŸÍf³3333aÏ s<츴˜N2ªŸ çÒd¤5ˆõ©&MšõçÃôz%€¸ÕQ¿“Õzq â¶kU›Ñb’üÙ°w`·¨×ëõ‹/^œ„u·F]?ê”Ëåòôôôt/ß³Ýí?|øðá ·;ÌÏ ôŽ)P¯×ëgΜ9cŽ$@ÿÕëõú§Ÿ~úé8Ö;HòÙgŸ}U_€.u;@Ú?@o‹ÅbÜõv{{{»ßË,ŽŠ¸î†b±Xö~2šL€4¼€Q$°{© P(†½Ð-5ójïIDATøÿíÁ€ÿ¯O¬¥?ÚÍ IEND®B`‚CubicSDR-0.2.3/font/vera_sans_mono96.bmfc000066400000000000000000000020121322677621400201060ustar00rootroot00000000000000# AngelCode Bitmap Font Generator configuration file fileVersion=1 # font settings fontName=Bitstream Vera Sans Mono fontFile=VeraMoBd.ttf charSet=0 fontSize=96 aa=1 scaleH=100 useSmoothing=1 isBold=0 isItalic=0 useUnicode=1 disableBoxChars=1 outputInvalidCharGlyph=0 dontIncludeKerningPairs=0 useHinting=1 renderFromOutline=0 useClearType=1 # character alignment paddingDown=0 paddingUp=0 paddingRight=0 paddingLeft=0 spacingHoriz=1 spacingVert=1 useFixedHeight=1 forceZero=0 # output file outWidth=1024 outHeight=1024 outBitDepth=32 fontDescFormat=0 fourChnlPacked=0 textureFormat=png textureCompression=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 invA=0 invR=0 invG=0 invB=0 # outline outlineThickness=0 # selected chars chars=32-126,160-255,262-263,268-269,273,286-287,304-305,321-322,338-339,350-353,376,381-382,402,710-711 chars=728-733,937,960,8211-8212,8216-8218,8220-8222,8224-8226,8230,8240,8249-8250,8364,8482,8706,8710 chars=8719,8721-8722,8725,8729-8730,8734,8747,8776,8800,8804-8805,9674 # imported icon images CubicSDR-0.2.3/font/vera_sans_mono96.fnt000066400000000000000000000705341322677621400200040ustar00rootroot00000000000000info face="Bitstream Vera Sans Mono" size=96 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 common lineHeight=96 base=77 scaleW=1024 scaleH=1024 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 page id=0 file="vera_sans_mono96_0.png" chars count=254 char id=32 x=1015 y=194 width=3 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=33 x=845 y=873 width=13 height=96 xoffset=18 yoffset=0 xadvance=49 page=0 chnl=15 char id=34 x=985 y=776 width=33 height=96 xoffset=8 yoffset=0 xadvance=49 page=0 chnl=15 char id=35 x=156 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=36 x=39 y=776 width=38 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=37 x=250 y=97 width=49 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=38 x=826 y=0 width=49 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=39 x=887 y=873 width=12 height=96 xoffset=19 yoffset=0 xadvance=49 page=0 chnl=15 char id=40 x=528 y=873 width=23 height=96 xoffset=15 yoffset=0 xadvance=49 page=0 chnl=15 char id=41 x=552 y=873 width=23 height=96 xoffset=12 yoffset=0 xadvance=49 page=0 chnl=15 char id=42 x=84 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=43 x=141 y=194 width=46 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=44 x=743 y=873 width=18 height=96 xoffset=14 yoffset=0 xadvance=49 page=0 chnl=15 char id=45 x=394 y=873 width=27 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=46 x=797 y=873 width=15 height=96 xoffset=17 yoffset=0 xadvance=49 page=0 chnl=15 char id=47 x=516 y=485 width=42 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=48 x=126 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=49 x=440 y=679 width=39 height=96 xoffset=7 yoffset=0 xadvance=49 page=0 chnl=15 char id=50 x=877 y=582 width=39 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=51 x=168 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=52 x=528 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=53 x=713 y=582 width=40 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=54 x=210 y=582 width=41 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=55 x=672 y=582 width=40 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=56 x=294 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=57 x=336 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=58 x=813 y=873 width=15 height=96 xoffset=17 yoffset=0 xadvance=49 page=0 chnl=15 char id=59 x=762 y=873 width=18 height=96 xoffset=14 yoffset=0 xadvance=49 page=0 chnl=15 char id=60 x=835 y=194 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=61 x=132 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=62 x=745 y=194 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=63 x=913 y=776 width=35 height=96 xoffset=8 yoffset=0 xadvance=49 page=0 chnl=15 char id=64 x=692 y=97 width=47 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=65 x=300 y=97 width=48 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=66 x=925 y=194 width=44 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=67 x=520 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=68 x=378 y=582 width=41 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=69 x=480 y=679 width=39 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=70 x=835 y=679 width=38 height=96 xoffset=7 yoffset=0 xadvance=49 page=0 chnl=15 char id=71 x=172 y=485 width=42 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=72 x=754 y=582 width=40 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=73 x=306 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=74 x=382 y=776 width=37 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=75 x=515 y=194 width=45 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=76 x=120 y=679 width=39 height=96 xoffset=8 yoffset=0 xadvance=49 page=0 chnl=15 char id=77 x=893 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=78 x=420 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=79 x=308 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=80 x=462 y=582 width=41 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=81 x=805 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=82 x=469 y=194 width=45 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=83 x=504 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=84 x=585 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=85 x=980 y=97 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=86 x=235 y=194 width=46 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=87 x=364 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=88 x=926 y=0 width=49 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=89 x=775 y=0 width=50 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=90 x=660 y=388 width=43 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=91 x=599 y=873 width=22 height=96 xoffset=16 yoffset=0 xadvance=49 page=0 chnl=15 char id=92 x=546 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=93 x=576 y=873 width=22 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=94 x=0 y=194 width=46 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=95 x=416 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=96 x=997 y=582 width=24 height=96 xoffset=7 yoffset=0 xadvance=49 page=0 chnl=15 char id=97 x=747 y=388 width=42 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=98 x=790 y=388 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=99 x=78 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=100 x=588 y=582 width=41 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=101 x=45 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=102 x=718 y=679 width=38 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=103 x=630 y=582 width=41 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=104 x=640 y=679 width=38 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=105 x=602 y=485 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=106 x=129 y=873 width=30 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=107 x=919 y=388 width=42 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=108 x=981 y=291 width=42 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=109 x=970 y=194 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=110 x=0 y=776 width=38 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=111 x=717 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=112 x=704 y=388 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=113 x=688 y=485 width=41 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=114 x=876 y=776 width=36 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=115 x=572 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=116 x=795 y=582 width=40 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=117 x=496 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=118 x=270 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=119 x=468 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=120 x=94 y=194 width=46 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=121 x=47 y=194 width=46 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=122 x=757 y=679 width=38 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=123 x=268 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=124 x=900 y=873 width=11 height=96 xoffset=19 yoffset=0 xadvance=49 page=0 chnl=15 char id=125 x=420 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=126 x=0 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=160 x=1019 y=194 width=3 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=161 x=859 y=873 width=13 height=96 xoffset=18 yoffset=0 xadvance=49 page=0 chnl=15 char id=162 x=344 y=776 width=37 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=163 x=301 y=485 width=42 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=164 x=874 y=679 width=38 height=96 xoffset=7 yoffset=0 xadvance=49 page=0 chnl=15 char id=165 x=571 y=0 width=50 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=166 x=912 y=873 width=11 height=96 xoffset=19 yoffset=0 xadvance=49 page=0 chnl=15 char id=167 x=192 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=168 x=338 y=873 width=27 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=169 x=260 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=170 x=98 y=873 width=30 height=96 xoffset=10 yoffset=0 xadvance=49 page=0 chnl=15 char id=171 x=957 y=582 width=39 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=172 x=220 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=173 x=366 y=873 width=27 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=174 x=52 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=175 x=450 y=873 width=27 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=176 x=309 y=873 width=28 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=177 x=450 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=178 x=280 y=873 width=28 height=96 xoffset=10 yoffset=0 xadvance=49 page=0 chnl=15 char id=179 x=251 y=873 width=28 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=180 x=503 y=873 width=24 height=96 xoffset=18 yoffset=0 xadvance=49 page=0 chnl=15 char id=181 x=876 y=388 width=42 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=182 x=730 y=485 width=41 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=183 x=781 y=873 width=15 height=96 xoffset=17 yoffset=0 xadvance=49 page=0 chnl=15 char id=184 x=664 y=873 width=20 height=96 xoffset=14 yoffset=0 xadvance=49 page=0 chnl=15 char id=185 x=422 y=873 width=27 height=96 xoffset=12 yoffset=0 xadvance=49 page=0 chnl=15 char id=186 x=991 y=679 width=31 height=96 xoffset=10 yoffset=0 xadvance=49 page=0 chnl=15 char id=187 x=679 y=679 width=38 height=96 xoffset=7 yoffset=0 xadvance=49 page=0 chnl=15 char id=188 x=561 y=194 width=45 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=189 x=282 y=194 width=46 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=190 x=607 y=194 width=45 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=191 x=949 y=776 width=35 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=192 x=447 y=97 width=48 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=193 x=398 y=97 width=48 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=194 x=349 y=97 width=48 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=195 x=594 y=97 width=48 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=196 x=496 y=97 width=48 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=197 x=545 y=97 width=48 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=198 x=100 y=97 width=49 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=199 x=40 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=200 x=360 y=679 width=39 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=201 x=320 y=679 width=39 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=202 x=240 y=679 width=39 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=203 x=280 y=679 width=39 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=204 x=458 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=205 x=610 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=206 x=648 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=207 x=800 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=208 x=788 y=97 width=47 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=209 x=814 y=485 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=210 x=572 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=211 x=440 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=212 x=396 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=213 x=352 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=214 x=264 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=215 x=856 y=485 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=216 x=673 y=0 width=50 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=217 x=176 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=218 x=88 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=219 x=44 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=220 x=0 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=221 x=520 y=0 width=50 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=222 x=898 y=485 width=41 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=223 x=880 y=194 width=44 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=224 x=258 y=485 width=42 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=225 x=129 y=485 width=42 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=226 x=86 y=485 width=42 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=227 x=43 y=485 width=42 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=228 x=0 y=485 width=42 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=229 x=962 y=388 width=42 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=230 x=50 y=97 width=49 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=231 x=534 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=232 x=225 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=233 x=180 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=234 x=135 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=235 x=90 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=236 x=645 y=485 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=237 x=559 y=485 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=238 x=473 y=485 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=239 x=833 y=388 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=240 x=673 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=241 x=913 y=679 width=38 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=242 x=629 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=243 x=484 y=388 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=244 x=761 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=245 x=937 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=246 x=849 y=291 width=43 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=247 x=653 y=194 width=45 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=248 x=150 y=97 width=49 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=249 x=686 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=250 x=724 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=251 x=762 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=252 x=838 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=253 x=188 y=194 width=46 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=254 x=430 y=485 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=255 x=329 y=194 width=46 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=262 x=80 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=263 x=836 y=582 width=40 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=268 x=940 y=485 width=41 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=269 x=796 y=679 width=38 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=273 x=932 y=97 width=47 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=286 x=387 y=485 width=42 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=287 x=982 y=485 width=41 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=304 x=154 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=305 x=215 y=485 width=42 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=321 x=0 y=97 width=49 height=96 xoffset=-2 yoffset=0 xadvance=49 page=0 chnl=15 char id=322 x=790 y=194 width=44 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=338 x=643 y=97 width=48 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=339 x=876 y=0 width=49 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=350 x=0 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=351 x=116 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=352 x=42 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=353 x=230 y=776 width=37 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=376 x=724 y=0 width=50 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=381 x=616 y=388 width=43 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=382 x=952 y=679 width=38 height=96 xoffset=6 yoffset=0 xadvance=49 page=0 chnl=15 char id=402 x=976 y=0 width=47 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=710 x=66 y=873 width=31 height=96 xoffset=9 yoffset=0 xadvance=49 page=0 chnl=15 char id=711 x=34 y=873 width=31 height=96 xoffset=9 yoffset=0 xadvance=49 page=0 chnl=15 char id=728 x=191 y=873 width=29 height=96 xoffset=10 yoffset=0 xadvance=49 page=0 chnl=15 char id=729 x=873 y=873 width=13 height=96 xoffset=18 yoffset=0 xadvance=49 page=0 chnl=15 char id=730 x=478 y=873 width=24 height=96 xoffset=13 yoffset=0 xadvance=49 page=0 chnl=15 char id=731 x=685 y=873 width=19 height=96 xoffset=17 yoffset=0 xadvance=49 page=0 chnl=15 char id=732 x=221 y=873 width=29 height=96 xoffset=10 yoffset=0 xadvance=49 page=0 chnl=15 char id=733 x=0 y=873 width=33 height=96 xoffset=11 yoffset=0 xadvance=49 page=0 chnl=15 char id=937 x=360 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=960 x=200 y=97 width=49 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=8211 x=208 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=8212 x=104 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=8216 x=1005 y=388 width=18 height=96 xoffset=16 yoffset=0 xadvance=49 page=0 chnl=15 char id=8217 x=705 y=873 width=18 height=96 xoffset=16 yoffset=0 xadvance=49 page=0 chnl=15 char id=8218 x=724 y=873 width=18 height=96 xoffset=14 yoffset=0 xadvance=49 page=0 chnl=15 char id=8220 x=0 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=8221 x=917 y=582 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=8222 x=600 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=8224 x=560 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=8225 x=400 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=8226 x=160 y=873 width=30 height=96 xoffset=10 yoffset=0 xadvance=49 page=0 chnl=15 char id=8230 x=836 y=97 width=47 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=8240 x=312 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=8249 x=622 y=873 width=20 height=96 xoffset=13 yoffset=0 xadvance=49 page=0 chnl=15 char id=8250 x=643 y=873 width=20 height=96 xoffset=16 yoffset=0 xadvance=49 page=0 chnl=15 char id=8364 x=315 y=291 width=44 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=8482 x=740 y=97 width=47 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=8706 x=200 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=8710 x=0 y=0 width=51 height=96 xoffset=-1 yoffset=0 xadvance=49 page=0 chnl=15 char id=8719 x=160 y=679 width=39 height=96 xoffset=5 yoffset=0 xadvance=49 page=0 chnl=15 char id=8721 x=252 y=582 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=8722 x=423 y=194 width=45 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=8725 x=344 y=485 width=42 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 char id=8729 x=829 y=873 width=15 height=96 xoffset=17 yoffset=0 xadvance=49 page=0 chnl=15 char id=8730 x=884 y=97 width=47 height=96 xoffset=1 yoffset=0 xadvance=49 page=0 chnl=15 char id=8734 x=622 y=0 width=50 height=96 xoffset=0 yoffset=0 xadvance=49 page=0 chnl=15 char id=8747 x=376 y=194 width=46 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=8776 x=405 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=8800 x=699 y=194 width=45 height=96 xoffset=2 yoffset=0 xadvance=49 page=0 chnl=15 char id=8804 x=495 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=8805 x=540 y=291 width=44 height=96 xoffset=3 yoffset=0 xadvance=49 page=0 chnl=15 char id=9674 x=772 y=485 width=41 height=96 xoffset=4 yoffset=0 xadvance=49 page=0 chnl=15 CubicSDR-0.2.3/font/vera_sans_mono96_0.png000066400000000000000000003043511322677621400202150ustar00rootroot00000000000000‰PNG  IHDR+ƒ IDATxœìOlgzÿGA| ¦t‰©urJjÊ],°P Ô†)À](‹…‘¶]a僅%ȧ&{‘‘ é j÷ Ѩ‹nŒÖ€èúQpu/?Xô&@D‹MăË9ô¦ßÁ;͘™™÷yÞygæá÷sJ,rø¾óþ}þ@DT«Õê‘—/_¾Ìf³Ù¨Û Åb±Hþ÷ööö¢n/*Èf³Ù—/_¾ÍùjµZuú~>ŸÏ÷z½žèûÅb±vßúóNÔ Àà‘Íf³««««”ÏÖjµÚôôôtÐm’L&“}®V«Õ*•J%Œ6I „JµZ­ …‚èsþµ@€ÐÈçóùùùùyÑç üêù‹¨€Á ›ÍfïÝ»w/•J¥¼>W*•Jõz½V»€AJÜ?„ 8 @à‹Å¢(îÂ?,P Pòù|~sssÓë3þ€àAF:N¯­­­¹Åý›¦iÞ¼yóf³Ùl†Ý6("N§ß{ï½÷¢nHŸ³m@®Åëì,8A•+ôϳ$õ¼A¥pvvæ|A¦Œ•%ø&iŽ%©/2¨ž[¾‡;®ª…oîG­„å*+œÀYƒhAˆ&;åÂ:A= *¹«¶ÊX=œ.‹Ü¾1WUõe‘N9X{'u¾ØÇg׋Î}¡ôÜ‚à{¸c«r?–ç¨<ýXýƒ|‡?€z9Å"~¡, ‘ÆŽò ÌÕäâ5þºŽ;Uk¶Ð@½,êú^3:XUYeúµÕ ³·Qyöâèèèˆò;ýócÐ׋®}¡ÆÜÂù÷6ÜñU5¿¢ö> ¢zNvžpŠ8PàŠÅQ´h)—1X_’ç®Ûœzp…í®F}§Q»Ñ>Q ­ª-€QöEDœ¡z……›0‡õ¢g_¼ÚòoÀÛDa‰Òó€Cs2°¾p7 lÀœØYýW~¶ø’æbá¾:ÿ=qY?^— ¯gzíkQÏ Ý¬í:*%@0„q9KØà^´Âr;…õÿ ²Â™èN){g•—$­—$õÅ"ÌŒðÅb±9À› ’ÉÇ¥ìŸaÄì¼ð›©0lkgƒ¡,V”? —°âÿ‘ØÑÎú¡–ÜŸ»~Ü6SJÛ¼ö¶(½zt‹·§Ž!.?ñ'Œ zX@“»¦`ý—G&t‹:>2J™ý+Ië%I}±àÎxóK–ú¸”ý“ik¤óQEfí0_vЉߨƒ%€<ˆÿŽ ×u?¡®¯çQ<^ýr~P•®aº&&Ádü|ØÀ ½beÍÑ™yÀUÚ˯dînIZ/Iê‹aðPÎ…W)#Z÷qIüg¼¾Gn¤TÆ ]RU^N)bhå@ü4ˆæ´ÊÑr ¬¯6SןÛáåæK½„…%LP‘X@ AÆÏ‡m”ñ ž °þË#s·äžÙ22{j’ÖK’úbPèŠ*¡=¬êªà¬Èe¿îÿA_ E‡|P¸èýD>€1ñÿáÕúm†~\¿â¬ ja)tkž ,QX̹÷êÚSÜ©n±;Èäo*ú£ózIR_’ÂKÎrgV½wqÇEUÒϨ ³Ô3#jE…÷‹ .¯v†!èy-Fš<ÿ>¢uôè5^¢õ£Â…?Î €°ÚÀ¨gbTs‡z¹ ¬é ,QYk‚ðÒ³`úâ.›Ä6Ìøy]×Ë ÷E×3LVóšÏ22*e¿ Ô¸”ý³CÝ—"Ÿ‡ªÜÿ-¶‡)|{½«È591"¬øÿ¨µ€:¡ÃúñºL{­Ÿ¤æPs¯*”€úœAXW”<ª=ŸÒ6κVm ŒÒZ£Ú (¯‚A h剌`cGvÞ%i½$©/œµª£ÑN…'¶ý}ª0îª?ÉûâTöÏ‚Óæ¨ÛªÌýß"ˆÐM`Œb{ ¯‘ks"Äïa6:A¡býp…^‡†¬0îu±ð“+Bµ0›Ïçó?ûÙÏ~Fù¬ª¬ûÔ=\´7S×pä‡VPÞi{H•TZ£ŽÕT)tÂúï 炬ë¿ÙûZ’ÖK’úÂý}îë*å0«_²Þ1ý¨¸#É–ŒcøuME.ƒpÿ_|ñeq©î”W[ÙIÏÙŒÜ&sä!Pè wýØñ{Ér[SnÏ•ucóZ»a…îØ÷Î!¤ÂšKÝóDÏQéZwtU†P×$÷"¤êò®C¬¦ªK£Ö¯yè÷²ëÕ¿(bÌ©ïOUتŸñJÒzIJ_âh-6Œ`îÎÿþïÿþï*Ÿç÷=ÉŒÍ'Ÿ|ò‰¬ç@”¨òì êÂïõz½Ÿÿüç?§`.Jn¿¡BëEi¿Ûá9¨.€Pè‰Û¸x]ðT]°ds·õCiSÿ!%ºÀû]«*~ܱò:ÈuµzGõl ;"Èvù½ÀGm´ðãr*ûŒ³3µÂg,‚ôöð3FAYïTYKý郎—$õ%nñâªC°ƒBÅ{ º¯º„&Rû¹€ë:vfC·ÃÊk2ªr{Mx·¶ ʹ(ôÃmŽz­O•›´L¼ êd6nŠzùµºs¯vé÷6AYÚý¤gwí÷Ÿ‘Q[íÄ5ñ”ŸýW4æ~ž…BIuúñ{HÒzIJ_‚Hl$ªä•0P¡à ª¿:ÝK¨gG¤Ê'ÎB±g“P1YÜ^¤×³UN0Ù¤Y:Ä¡„ úÁ]?ª5´¢MÙMÈ’ÍÀ{ès ýæ7¿ù å¹~:¿7î»ÚbºV Ì?{w}YóR  EO©ØÝö5ç3ç^Ä;Tå™f¡â”õ’¤¾pçzTwÁ¸Xÿ-TÈ5*rw8¡C(‡õ®©œH]$öÅÊÙ€UtÎiC]¸Ü61§MGÔÑF%caM*Pègý±1SÖÓf)[€ uoRi]wïK·Ê,ÎP¼Ž”Ë…Ÿ6É*”¶aI²ö… Î2Ye¨ ý—a•ϦŽ[!ªVS~SD\ÖË õEæŽÅ:Ÿîª~ö¨þ3<ì˜õ8$ž÷u=D²@mdÿe9¬0·I)ziå]ÈJ:i¤Â ½à®ê~àµÙö¼”=ÀíÒ!Z?²Eøç<ÛïáC½t¹µ›ûÜæ=õÒ4HÞMA Û2„¡¤áÎ)n²&ï,7×™p(¿•Õ\E;AYKU\Âã°^­/2ë)̳M…‡™Œ’ͯH…\£zOÒM֢νÈÚÍ™8~´É~:è$PR6€—ð*: dܘ“zá4n}¦\®¸BE>ŸÏûí·ßŠÖ«Ûa@Y?œK¡h¼¹óWÕæí'Ö\ö uj»êÜI hw{.a)iâ¬)¨XÒãããã ž+{ÿ%HoÕ{óÙ™šD§ßþN_TÌÅ8¬—Aé‹_o—°„2Ê{¦œ-\kºWÿ({…ªñSµ/é&gqîd‘)dÜÿíÈzppšØ!DFàµE^&Qá "š7” OÅ3Îú"Ao²²kÝÂëÐWNÄ{ðc ½ì8õƒú¬ARP/ÃaäaŽQP¶* `Pí ·~«¶†ÙùÍo~ó› žM9cU*¨ÕId¼7Tígº¯—Aè‹ ¡2¬÷Mi+åÄYg¢¾Qž¥òäwž…u×:Wƒè¿ÃíÐÇü1åsOŸ>}Ún·ÛýÿþÙgŸ}FùþåË—/Ë.¦÷ßÿýþ;888èv»]™çy1666–J¥RNûꫯ¾òún·ÛíôÿûùóçÏŸ;w6Æl6›½|ùòe¯Ï?{öì™ÛßÓétzlllÌë½^¯÷üùóç²íL Ôõ“N§ÓW¯^½êöœN§Ó¹sçÎ Úhá´Æ8ë§Ýn·GGGG‡íßì åéééi.—Ëy=Û4Msjjjjhhhhzzzš×31Ýn·ûâÅ‹2ß½páÂ…áááaî÷ÆÆÆÆd•“¢ý0I<þüy¯×ë‰>÷á‡~F{¨ã­bŒÖ×××MÓ4ý>§Ÿû÷ïßwºS *—.]ºtñâÅ‹¢Ï•J¥’}_k4 ÑwjµZM´¿YŸã<;•J¥EÏåàvn‹Åb¹\.‹¾¿½½½ý‡?üáܽÔ霔!Ië%Ž}©V«ÕÝÝÝ]¿ÏÉd2™ mò‚rf¼xñâ…HÞyöìÙ³ãããcÊo¾zõêÕëׯ_ûy–Ÿ»C?~çÙöööv³ÙlªhKœa)(‚˜…› xxxØét:¢ïg2™Ìäää$§}†á.èuùtSˆt:Îááá¡èûNí¾páÂíK”Ë«hã\–Ü”VƒgýˆÞiïóÅ‹/úÕëÇn=¥\(­Kñðð𰇌“BÄKqÉ}e}š¦iÊ*+âH»Ýn?}úô©ès"!Âo¢G Êx«£f³Ù|òäɿϱÓét:[[[[*Ÿw®_¿~ÝkL-d½^¯Ûÿ}zzzºT*•üüv§ÓéŒW*•Jÿ³kµZÍë»"ãŽìÞd'›ÍfWWWWEŸkµZ­•••?¿å—$­—¸õ¥Z­V)g:•B¡PÚ«‹¢xúæ›o¾}†c@ (D¨4l6›Íæööö¶Ìwq–|K0999™Éd2¢Ïy ¿Ô‹‘aн ìœ;wîÜùóçÏs¿ÇÅr¿+ §¿ûÑX¦R©”ÈZ=Hˆ.:†!ö,¡\*(›fÒá¬Ñ;¥zûøÁÉÒªjýXnf''''¢}Ϻ õ_Šƒ„2g"²–gYÅì B‘U„* ]½zõª×s(ã­ÒJµ%Öÿ·y_†·•«^¯×)žN˜¦iÎÍÍ͹ÇÊÊÊJ«Õj¹}?Œ=dcccC´g›¦i.---áÊ%Ië%.}Q-ü[,...ÂKñd5 ºÁ“zç}Ž¢LPm˜ÙÚÚÚ¢“ûY^^^ÆYò–À¯û¿EavüZ62™LæäääÄnqñrn4 ªàdÁo#ÒxRÆW4wMÓ4?~üX¦}IGfý„eñ}ýúõëW¯^½Rõ<»ÅÕM¹gÇr¯u YÏ&¯5õå—_~éµ'õ “!uÃk(c#²ŠPÏ\Ñs(V#‘›'•–@Õ›éééi§/DVm*­V«5222Âùm§½E¤¨¥¼³;wîÜ‘¹@ß¾}û¶—gS·Ûí®¯¯¯{=#ÈЗb±X¤ìÝ:¹ë¼^¸Ä¡/Ôðr¹\îîÝ»wƒx6Å@3(Þvív»}ÿþýûœï´Z­Ö£GÕ¦¸AV¨pÿ·: *,—» â}ʼ ˆÿ^Ø“iíïïï‹„ÙF£Ñ°.çýîµ:Òï!Z^ñë2±¯*…˸@QîzYE¨ÖUÏQ'G•%P뿬µÉŽJ‹³(ì†~äZtÇS?ßOœ\ÿûIÒzѹ/Ô9â‡ùùùyJ’9J2;{"7ÙÜ=n¨ y ¨ölÎf³ÙÙÙÙYÎwr¹\îÆ7Tµ!î*Üÿ-8aAºÓ¨ÂÒêëó›$(žè²ƒøà„•MW‡„~²ÈxyYLÓ4¿üòË/½öç~·õ°ÙÅ J"@¯K'GÅsTçÉQa ŒÚšiÑn·ÛËËËË~žñäÉ“'ªîªÂ¯¸ŠõõõuŠC䙥2!˜¸¹þÛIÒzѹ/”9bÇòòã„̤R©ÔÚÚÚZ²Ë ³8ãh±ºººvu ]!+T¹ÿ[P¨‹/^¼téÒ%ÊgÝ:¦>—ËåNOOOeʈr¿µIuA61 %þ_¤y 2þÆGfý„•Ç‚›óÃ>^”Ì¿º%ôëG&Û¼—RͺŸÏ_¹rå å³ýI4¹!3”Pî½SE‚Ì$Ïçóóóóó2ßÍd2™………ÕmŠ#$€J÷ j@*•J]¿~ý:噆¡>&Ø0ÞNð5>>>îÖîr¹\VQvPbx(PâÿE±ûˆÿ§ÃY?"ë³LO.NÂÓú±Ê÷éœÐO™ýÎK©f)p½¬ÁýÊUY‰“Ur{å§7«j”9üXu±fZt»ÝîÒÒÒ’ŒP£³ÐIõP"¢ºÔq>ŸÏonnnŠ>ÇÉÏIZ/:öeqqq‘º§öçºñ¢†DÕƒ°\.—EF¤ r*8ÁG'fgggá@T¨tÿ·à„ˆ2Ûq»t©rSµê†»¹•Ëå2ÇÚíÔ®Aqá¡"vñÿ<8ëGTûUUO/œ„§ñ|üøñcÑAuB¿ ± ™^»%¨‹.Üý4¨2¬ºã§7NÚM¨¢ŒU9d-:Y3-d„š°3îyF¹‹…©$ç†5 ÿö·¿ý­H èt:;wîÜñßÂ7¥ÔLÒzÑ©/ë£Ñh8å÷áVÏP $å¹ 9ŒêéãE&“Élllløm‹ˆf³Ù%zššš¢¬»qŠ‚(\U¨H§ÓéÅÅÅEJg¹qÔA…8-Ž‚—;5o[)ŸAL˜å„ŠØ}Äÿ󡮟n·Û=888p{N›¬Óâ´~Ü”ö¬ÜqHè×ÕÊl ™"…˜%¨‹”;A%ñJ²•8 -ÜRÆ*È Ý„fJâ-/¸—O§*B^¾|ù’£Hå–Û¢xxr=1Ã$•J¥(a`^å¿dÖ[Pè¶^ü S_¨U¢p B Ž<*âÊ•+WtñΠ†v¨– … ŠeÁ­ûT€Ó¥KE.;^±sÔL“nïvãeP»düRᬑe½P(ŽŽŽŽ8Ê·l6›=:::]zÝhNëÇ®¬°'ô›˜˜˜ˆûZãÌ_Q@K™ R,XÞ”‹ó ‡4É–y圻Nç$U° zäZu´f&j2-•F”°n]?éÕ”¤õ¢C_Üî N?{öì™ÛßU†P”³öy6è woܸqC”°™J”‰û¡Ž«jÙP¨ jÍ8îÿA…8)‚Ð`{ÅÎQ¼ÜÞ-W‘'8Ió(IÚD±I*ž¡"¯Cœà¬ІßJ’)RX–¹“““ŠEÇMHr[?•J¥â–ÐϪà„½ ¬<NÈ&É4 ž•Ù+!\ÿÅÇK(äX9Ć’¤Ñé]z).¿üòË/Ý.Ô²žƒ¢W89:¼à”Òâ”ÐòcQ§&6¥Bqýç&“ñ‚3¾”\Q„ :A”oÔ¥, õÜV­¨÷Tp´f²nÔA„¸)T‡øñp{·2ŠTÂ]?T—8Kà&s]kÁp×%”{)Š …BáìŒïŠkoÖùÑÑÑÑÙ™w’œÝÝÝ]•Ê7¼Ë~í²—w‡%dàâì %I£“Àæe8888púó PÆg=4⊪ÜRZÔðF‘‹—%Ku"g/×Y°fâ§¢ Uê7€zƒyö=²eÿD rY@O@îÿA…8µ'm×%Ù+ º›+ âÑpÖO»ÝnÏÍÍÍù)ùÃÅ-¡uýXž(œÌµV,/ÕZŸÏçóß}÷Ýw—µB¡Pèõz=NlÅÍܲ2{ ýÚe‘%ŽZåasšˆòdXô‹›E ÓétêõzÝÍs®?Ÿåâ;ÈqE´6)ÆŽõß‚z‡yކvGuýç–ÜÄš‰A”Ïó @™sý^yƒê©å§ìŸˆ¨Ër¼¥T¿§ H÷‹ ÂÜjöªÖöx¹@»%™ðreI²û?ˆÜõÓl6›7oÞ¼–À­ eýd³Ùìááᡬ6ywwwWR,‹ûûûû2—ŽT*•zøðáCª€â2kY™½\Íú‘%nlllì¯þê¯þJt‰ôœ&”CÛ>.^–!K™â&@e2™Ìäää¤õÿ”‹ï +hâŠhmRõ ™=Pt‡)D¨‰MEp²þsD¬™xT켟PŠ|5èg§·ìŸW˜œQ–¤zQá âªÃýß‚š$„Ðív»N™<ƒÈLîÖþT*•rÊXévð¶Z­Ö£G©l2ȬŸf³Ù|÷ÝwßuR¨Ä­ eý¤Óéôƒøu%ó*÷I­IíG @u™qÓ4;)qEÖkÕµ¼“ ÅCÃîºïe²âS½<Ïì—]ÊÅ—ÌøAñ,ñÔ÷ööödKie2™Ìƒ¸cDŠQ’5ÃPã!Àqýç ˆX3É…;dB¶¶¶¶(Þ7ºkµZZz΋J¥Ráþ6·ì_«ÕjýÍßüÍßloooS¿VY@'¨ÞGAx¹*¨îÿ¢’D%§,Üj7ܬ˜…B¡à'éV?^íï÷p›Ì¦išKKKKý‡J»ÝnŽŽŽªX|QÇk’<>^ëÇÍÞív»ããããœC‘Š›pí¶~úÙÙÙÙQ•EÖé‚N§Ókkkk*Ü U'úÉO~ò7M³›EËËz=<<<ü“Ÿüä'¨²á ÅCîLñ²FZãáõL»7%¹.—LÀÃ+ ±a¼¹ÔÚ÷(+ôÉoí\.—ûî»ï¾³ßm¨Ï¦$YóëêZ«Õjœ;7yæ ïiI†;dB~þóŸÿ\d„#X¡*‘)û·¾¾¾Þív»¢ý±Ê:ª×‘WÆj;Ü_~›ˆ+ŸÏç{½^¯ÿ9Ü8[¸µ%Œä_ºÀ©  ƒVÀŽ×œåÄ»=ƒóL¯¹CY?¢v8í ¢šáý¿ëUMàìì‡û%e=PÞ3¥¶ùñññ1µÔwæõLNû“Œ=¤ö¹ç6–öÏx=Ó:#¹¿$œ=àìLï=W§¾PÖ½ ¿ùÍo~#š;2Pç÷ÛáÞI)뤟 ÷4æ˜_tè U¾8;“—iT¯E§3™ò.92’è¾bÕü⌛Ó;ã~_•<Ë:A´ÍÑ€ãþOÑæRÕ·èqÑl6›N® Ü8[¿äóùüÇ:iÛZ­VëÖ­[·ÂhÜÖaÆæææ&eý4›Íæððð°¬'‚axÇíS×WÌ]«Õj½ûî»ïö—œžžž.•J%·g^¾|ù²uØŠ´ÕF£1111aß/-¯8BJæmŠEêG?úѸßygy=¼ÓlåhðJdwör·>Rb ‘Ì,Þpk¬Sh4ßýîw¿[ZZZRýìííííþ=Ö ªWh?TO0;œd׆JMq„ãQ"›,œ à…ÏjN‰^*²åeýÀMRêôÎVVVV8!©Q”¤†œvä¨à¸ÿ{%sáÀÙð©Ù§-Ü&AXJ/á_æÀŠ3"—ù‘‘‘Ñ‚ít:ñññq/AR”˜Å4MsjjjŠ"”ÊÄ-% ¯õ³¿¿¿´5ÄKø§®/¥f§ÓéÌÌÌ̸=Ã+Ñ]!éµoŠ”·nݺå6ï)±ö~]fݾï7!ʽ¢ ùðÃ?ôÚû/ncf)(±…Hfo¼´2Øçñì­­­-Êge÷ª‚Á5ÙµÖLò¡–»´# àÆ“'Ož8ÍcJ8ªðÙ_^6 ¸IJÖ¾[.+/Â. HU®v䨠nˆ”d.T¨å’ ãm«õÙ3333NÚ9Kˆ ÊÅ¥Z­V½²ß¾}ûn IDAT6÷ÀJ2«•è¦ÔWU9w“Ž×ú1Œ7Yñƒ a)‹Å“““·ƒ€º~¼æ%‰©—¥Í:D½â鬸4·ç{T\¯'."‹–Ÿƒæ7P+LNNNºÍõþX}¯º~øá‡”øN$3‹?•J¥ÂÉDîEârú›Óòº ^½zõªÛ³ì ™Y2ðò ¢â¶ŸNOOOûjd \¡›$Ú0 caaaceTéõ ƒZ)Ç5ı¿¡^^,TϪeŸj}ÚÑ·ìßýû÷ï{)¹¡Róóóó:% Ê“ò Šf5HõFd€aˆ…˜L&“9999ñ£°/«¥a@øwƒ²q‰Š 2_ó­Ë›Æ"ÀüOOOO½²õë¸~Üæ.Õ îåÞ'Z~\EÊ0îEÜÜeß@­àæ¹ä4‡¼.ƒ^ϲåN2èv»Ýk×®]“UˆöS?JÙ½ºÝn·Ÿ>}ú”ó¯ 5ýäóùüüüü<çùðŒ'c ÙG?¡­V«µ²²²âõŠÒ–"Øs¬Ï†!'sÙùíoû[JLÙ?Ñ;ã†3É(e x<ÉÔAb# 2 À¢Ýn·?øàƒ¼JKpvF˨iÏÌ)ü­ØsÝ„ $Ÿ¤$àQe‘?„²~¬ƒó쌖õÙž…Y$øë¼~Ü„)jR//‘ &cá°](ü<ž6o ¼ÃL&“q›ûna*nŠL¯gY`LV V¯¤¥ý´Z­ÖÈÈÈe?%Dõól'(¡|N”Ëå²H à•—É Q(Ъ‡q?ÜG·RÅ"D¹ˆ,(J[JÌ>7ù¥l™<ë~Gêý”ý}NDz€oeÃ)”2n%Úü&ã–ˆðK%&âeþ‰Þ¡ŠgqZ?A–´.›^å\({•ŸïË”²:;£—å’-q¤syª°ñS&Êí=ú)—¦2äM„¥ÀT—¾Yv8Œ’Æ~˪¹ÍoÙç†yoˆË£ K_dÏH;^s@Å}ˆ²v(w\ʹ.Ó^î°·•ò]¿eÿDèV0Ê€ÒÑMXJA¯…M2ÿ(š}ÑaTºA$.ëÇëð§–^—F«m^sO´S꺋ú(s±¥>[ölÐùb6²ïÐk~ú¹ÔB ‘#I}Ñ•°•˼Ã%iŽéÔÝe®Ç¤^û»Å1õÜè_Ã*ŒwÜw%ûîì¹î©û\P2Ë[!~c<ÂF6 ÀŽU–njjjJuÍ[Ã0ŒZ­Vóªo¾ñÿñ#.ëÇ+¼(•J¥ÖÖÖÖ¼4ûnîkö¯&¹\.·³³³ãÖ¾®ûw?2 ݨ1ú²Icþ=²ïÐËýÏO™F„g‘‰ÍšV«ÕzôèÑ£¨Ûü¡"Kˆî"†A•Îår¹ï¾ûî»~!¹X,½*“Qž{zzzꤨ± Ø^ ÐPQöODÊ:x²Þ¸¹ÿ[¨Öªp:;ƒÅŸ å½S¬•"+¨ÊLõà‡è¼~DZo§¹!šOýûÈÂЯ§ì»Ôw!c5£j–eάµ·‘=cE–“8xgèdôK’ú¢ªÎ•D±%iŽéÖ—8È:¢w‡>ô£Ò#Áoø×[2¨9ImGP¿ÿÔìÿºqõêÕ«*Ý­D:C†šôÆJxc}”l˜"k%%i²ø‹ÎëG” Öž¼ÐÂ+qSíÙG=òJhiÏ­ç‹’†r,O2ÖvªfYxÙdŠ¢²§2ž(gtÄË**dJ}i·Ûí¹¹¹¹ <U1;;;ë%à&éq«õP«PÈVРdÔ—©0dá´veÚÔ¼àVÑQU„úN!·€DÓ1æ^€©z\¬ªr)(¥j:N¿÷Þ{ïq¾CY2í¥ îQ+qt‡£Á»‰Ç²Âêf™¿p¼b‚ôÖû‹  ‡v»Ýº JÅ2Ã@âr ¶prLPò3@3¸ÉÄ ™ÊHT h€U±D”©_¶LiØåzà@ÉR•„ÿÿN€˜û÷ïßo·Ûí  B:Ngkkk+è߈åååå ­ÿ†€ˆàÔÄÕ$ë€F£^¯×Ãø-(€hµZ­[·nÝ ë÷ B¦Õjµ®]»v­ÛívÃúM(€©Õjµ‰‰‰‰0… 2q¤X,eï GGGGét:UÛá©×ëõ¡?3>>>Þét:^Ÿït:ññññ¡¡¡¡¨­þÕq¦Ýn·GGGG£nx@¨È&΋[¢ºjµZ¥öíåË—/³Ùl6ÉíÉboooO4ŸööööDÏqÛ(ßhEpp¢×ëõòù|>êöSàdÖ CÐÉçóù^¯×£´'ê̾NP•FÕjµêô}jÿuP2¹­æ?g ‹Úí66nc ˆ k´: §ýp<Ârtm…¤+¸^1Q**d×°›¢ËM™ Û0ðSo–"HèÕ:…€C4u²6F²²ë"Šu ¾ãö޽ú®ƒ7@ªö/ÿò/ÿâGЋªu4ÊöS…8ò$U7¿í¥¢› @„*¼YÂW:NQ?¯ T!SåuLtñ¶H¢ Ž1²ù;¸Ä!ÔÐE˜w³ö‰„ ¬„\%‡P-qRXÄE Â•>ìv{µÙ.°«Ä‘˰ÈÒ'ú¾.nê˨ŽBM˜$M š×ýýµ?ŒuàÖ·ß–õp€õ€"¨®Ë¢çD-0P… Š êwœ$€W_DÊ/ESÐmw{‡^ë˜êeBí?„‰H¹¯‹ˆ‘ÀÅu+=/ªXuŠ`©³ðoAÔ¢Ì$€ŸÌ÷^ãôø¸½CÊZ¦(É üt‚rî@âCSVÈ ê¹²ˆ„æ8 4"kGTÊŒ$)ÜÜÿ©^nÂtÐ^n¿Ëyg~”&Å%€aï½÷Þ{Q¸‹ÿêW¿úU˜¿—N§Óï½÷Þ{aþfÔüô§?ýiÔmˆ;nJªÌÅÀ‡’ÛûntüEÔ ð=ß~ûí·Qüî?üÃ?üC˜¿×ý3aþfÔüñücÔmtž?þ¼×ëõR©T*ê¶@Éf³ÙË—/_ŽºÀw¢n@\¸páÂðððpÔ퀤‚}V @,øæ›o¾qú÷«W¯^¥„ÎŒÁúÁqýúõëØgõ Ä‚¯¾úê+§Ïår¹7nÜðún:N/...:ýíéÓ§OÛív[E`PI§Óé«W¯^º"(I íÄ¡"( ;NÇéo››››^•#vvvvr¹\Îéonžè\ºtéÒÅ‹/z}¦ÕjµFFFFFGGG¡x²€ZFÈ"ʚǃ¥uРÐJVZ'tÊT‹> ªˆcÚívûéÓ§Oþ–J¥Rûûûûýg‘•ù¿P(œ¾×ét:[[[[A´—BÇ¡îQI²Ê©|‡Ö{¡Ü ½J`RJ€Šî~n¥;eˆcyØAƒfuppp´$À\9LÕþÍ9?ì¿ €„pîܹsçÏŸ?u;Ð?Yi3™LfrrrRu›¸ èƒ*â܇;wîÜqó0 Ã(—Ëeû¥gßëBº¼¼¼•*®ãÐ/žœœœd2™Lmþ¸xñâÅK—.]rú›n±Ü–’Ͼ¾½ |>þøãEŸq åá@B˜œœœÄá €;~³ÒRµ AÐUĹív»½¼¼¼¬âYF£Q¯×ë*ž%CÜÆÁ²6‰”* >¤R©”SnŒ¸Ärçr¹ÜééééÙ¼£†¢Ðìt:ÃÃÃCÕ¿Íõ4ÙÝÝÝå<www×é9qõB‚ !|øá‡FÝo¼6Ï$ª~-ccccQ[ÐôAqïC½^¯—J¥’Ÿg4Æôôô´ª6É—q°f¹…Q€xsåÊ•+ý®õ7nܸá–3CW …B!)w–8B1F"áª@Þÿý÷£nˆö˜Ãr¹\vûœu¨Æ5þN…%ÃËM2 Ї7 þIB ÿâ;j%F\Æaoook-K œ¸ü¸Z ÃÙ @O%Y¬;KœÇ$ŽPöäÏ>ûì³0Ú¼ ø‰!ƒKµZ­rã6­ø»¸i×UäÈH¥R©±±±1Umâ‚>¼}ðOúáÙ‹8ŒÃÞÞÞÞ YýíIµ8g¤ÝEX&)r»ÝnŽŽŽõ1222ÒjµZNß)•J¥þÏ QÂZþû¿ÿû¿íÿùòåËV‚°þ{¥išæ«W¯^qû5»»»»q»¯ÄÊž”û?à@ðC½½½=/‹¿ˆB¡PˆSâU92¢´ˆ ߃>ø# }P%<_¿~ýºª6qÑ}ªÕjuP„»¥_EŸ­D”:WGúÿøÿ0MÓ´þ?“Éd Ã0ìsóøøøøÛo¿ý6Švú¥P( Jù¿W¯^½zýúõë°ÚÜ è–¥è*‹N.—Ë}þùçŸÇA  *G†ÝB6èÃ÷ þHBT ÏQ†Ïé<ù|>????¯ò™:b•î ªŠA¹\.ëT®ÑÎ_|ñÅ“'OžØÿmvvvöÇ?þñû-¹ÿó?ÿó?¡6P!PEIbù¿¸@@ü? R,‹*-:¹\.w÷îÝ»ªž*3_¸pႊgq@Þ}' }0 uV﨔:C:N¯­­­%ݰP,‹§§§§A'ºËd2™?ýéOÒ1Îúúúz¿À§Ÿ~ú©ýt:ÎÖÖÖV4-TG¡P(èì‘g(û™išæãLJÕ&à 1ñÿ€J6›Í®®®®z}¦ÓétÆÇÇÇ)±‡óóóó:^l,(niT¢rFÞ}' }H§ÓiUqï™L&3999©âYt‡8fçR­V«a&6L¥R©‡>Ôí¬|öìÙ³ãããcû¿õÏ¥$em×ý¾W(!YÇÇÇÇÏž={V›€7PÄjü¿=Q µtR«ÕjŒŒŒ8%˜q¢R©Tü÷(\~úÓŸþ4ê6„EL_?F£1:::j?è»Ýnwbbb¢V«ÕܾçVCXÆÆÆÆTZ²¢ð¸A~ú Gú Rx6ŒhÊèê<²ÞNwŠÙÂ-¡”dwýøÍ#‹ŽJ€n·Û]___wû»iš¦×ßUcš¦9555å6Þ^w ºßWâ %$ëÅ‹/‚tÿ¯T*ÎÞÁ-#ë'ᦎ@s(—Ó4Í/^¼«Mq¡X,ÿó?ÿó?!.,›ÍfggggÝþÞjµZ·nݺåö÷J¥Ri4 ·¿;ÕÖêeöË/¿üÒî éF.ÃèÃAäHBTç½¹zõêÕ°s™è:2^…–çØÄÄÄ„îñ½QW5H¥R©µµµ5rçv:ŽÓßt³ÚZBG±ÔO¡P( < Z(ûÊÿé1‡²èz½^ïùóçÏÃhO\ÈçóùÍÍÍMÈ_F{DÚÙõõõuÑÅ­?VÐNÔÙ´Ýภ?|øða¿+¤a» £Π|’Ðà[»?ýôÓOÝ;a—Ôy¸É;Ngrrr2.â²Â¿Èr|||œ2Ï,tËÓn·Û÷ïß¿ïô7]“¶YÞ‰\+®E”L’Eiˆòú¡…àÿñÿQÇ ©ºC½D$)~KN Žr¹\î믿þ:©óÐËÅ•º1;Å ÚÑ1%ÕU¸Óét>|øê)¦Ë0úàúÀ# } Z¨MÓ4ÿë¿þë¿(µËS©TJUN :W0Z^^^ŽÃý‚›×žGäÙÐn·Û££££CCCC^žrvfggguºolmmmõ+1âü¯^¯×e”:{-Æ J(2äýˆT`Õ]ýÛ¿ýÛ¿ÝØØØú÷¬r/gp˵T«ÕªÊçq ^"¾ùæ›o‚øý°(‹E¯wlAÍ³³ã”àHçL½~ñΩs·Ûí¸ý}lllL7/ ª«°U›–ºVÂtFÜAx$¡T õñññq³ÙlzíYv´ê:ÜäŠq±êÙ=þ(”J¥R>*ÓÓÓÓSSSS¢°¨’OºÑn·ÛOŸ>}jÿ·¸mõz½NU¼XèêµG(ûYrˆ%CºÉƒÞë‡ÈÕjµj¯»TLN>ŸÏ÷z½ÞÙÙÙ¥ÜK&“Éœœœœx ”öI'J$Cyž,Ôøÿ8–ÝØÛÛÛ³15So¹\.‹~µZ­zYR©TêÞ½{÷tÒÌy¨^ –›ããÇSâmÃtFÜAx$¡T+·•pꫯ¾úŠòù0˜ºŽ%“·8ˆœ’†–ÕßoR¯f³Ù¼yóæMј-...ê¤4·‡ù…üÏ/wîܹà Ã0Œhr$°ËÿÙ ¹vÒ‰B¡PÚgBWXƒç$8onnnª²¾Zäþþþ¾l² r¹\î ïW\pŸ§r"&-þß¾°ý&é±¾]P,‹"…išæÜÜÜœu©yxèÌ '¹á$³²„„çÏŸ?ïõz=ÑçÃrF¼Aè$¡”˦…eqzñâÅ ”:7þ?I½îÞ½{—RÒ°Õjµ>øàƒT)4šÍfs{{{Ûë3açžÑl6›ÃÃÃÃCCCCÃÃÃÃÍf³u›¨xå1pC·÷G(žÈªIV«Õ*ÅÛeˆUm„;¡*ŠÅbÑkðTeGU™å5—Ëå>ÿüóÏÓétºZ­Vý–ŽÉd2™ÃÃÃC¿J€¤Åÿ‹æ†,–"à÷¿ÿýï).€·oß¾§C¸C½ÌÚ«d¼~ýú5%fØ0ÂqFÄ 4’Ъ…Únqå.±Ë%XçqàäˆCu!Qõ‹V«ÕºvíÚ5ÕÉîœâêí„{"éP=e,ðþýCqÿ÷›HÒËpÌ¡\.—ð=¡)öööö(®ÜºeG5Œ7mú·û·›ŸŸŸWñ¼L&“ñ›ó€zŠCü?unøá—¿üå/E›T£Ñhĵž§¯y@-%²¾]ã• õ2k×N‹òØ Ãe}ƒ>ÐHB¨Â³Ýó­Ûív©‚j‰L“0†ï Ñ|ét:™™™™ Î.ŠUÙèÕAUöÙÁû÷eϤ†a9‘N§ÓŸþù窌ƒ…B¡O€7®°âå9ùùùùyݱ}ôÑG©¬;ì7çõ"ägá…AÔ5y-Z­VëÖ­[·¢nGPxÍj2"‘«—NÊ&Ž«p¿â‚ºf‚vDÐU$¡†A¿¬÷{¾Q]Õ©ÊPYtŽÄJP(ó;a@ µºŠÈ*­còܸÂQöYàýËFù?·¤Ý~(—ËåA‘5Œ€2ñòF£·Ø#Yü$€¡XtÏЫ‹ð¤@½\)sqqqqÑM ¦[²IN2«~Å…è]Yí2Œ> ªHB8êûû@ÍtfvÇ[@wDwÏ0¼þDùΟ?þܹsç‚là ÁÍK1èï?N§¿øâ‹/dŒ®C¤Ÿ0dnéN««««ƒž00@¡P(pã5J¥Rizzz:¨6醬¦žjEÐYC/ÊÆýIÿ’ŠÈ1—ËåvvvvÜþ.¯'Ož<ÑIilj³íW\pâmƒtFÐU$¡Ô²·†ñCk9Ç5˜Ï% ã`¡[È—Jì§ÓéܹsçŽÌ³½Jõ[9ãüCUöY _¸páBmÒ+QúééééG}ô‘ŒÒâ‘%›(4›ÍfWWWWe¾K!“Édþþïÿþïƒz~ˆ¬  U¥W8¿5ÔG©T*©|&¥&©¬¦žjEð›x#(òù|^U>¿ RÒ?QB¢B¡Pè¯RAI¾¢c¹ ª«°S+Ç0H—aô}PEú@I6eΞoœ>YLçqà–ÔŠ¢åþýû÷9ŠK`yµîîîîž©/û hP+fX R"@{U«~ƒwߣx ùIJñàþˆ\P«Õj££££A[`kµZmhhhÈí·êõz}ddd¤Õjµ8Ïu+3===]«Õj¢ïËhêÊÿÏår¹ÓÓÓS•%ç85yƒ¦Õjµ=zô(êv„%!‘U.ÅWJe†ííímJ»eÅÏå‰ãÊêæžFÕZe=@Þ€>ø' }0 úYéæùFÍQT.ƒ¤ŒCXÂ3…^¯×ss[)Z:Ngkkk‹Ò¦|>Ÿïõz=®Ç¢Si'(cÕív»ý†«¡¡¡!‘áÌÏwû™žžžvzN¥R©pž£X,­5ãeÈá†BYþZ½ø#2€išæÔÔÔTÐG«ÕjŒŒŒP~§Ûívgfff(qvëëëënVv‘ÅÕ0ä4õqŽÿ¿qãÆ Ù„ÖXªòÞ Tœ¨T*§o|||œ3OÜÚÞW åõ\§Ã½R©T(Þ)œß_YYY‘ý¾]è·[Vfggge-‰Wa7¡€êFTÜ3úðôÁ?Iè5¡›a¸{¾QK„eÔ}.\¸paxxx˜úy’¾Ú¡Ìj\r±X,îïïïË,r¹\®Ñh4t0x„[x'8•J¥ö÷÷÷½”?^¥ÜdB.‚ û‰KiuvvvF­°ÅÍdù?ª‘Ó §;1÷¾>(D¢+Ñ_©T*MLLLp& ÅJj!²ÛívûéÓ§O½žÁÕÔÇ9þ?N§¹ß³B,ÜÆ²^¯×‡†haýø4ãÊôôô´ %€líd7¡ßN&“É,,,,È´klllŒzáró’áÄ á2Œ>¼}ðOúÀ¹ºõãDi°$ŒC (2(žÅb±¨¢<ñÅ?ã÷9ˆ°B6e•VÔ}"‡øI -³ÿZFe'9¡Ýn·GGGGý„z'‘Ða$ú{õêÕ+?9¨–Š-Ò’s­ Ô8=ôp, n!ª:ë³®PCTÜh4 Žr"ôÛ)•J¥ý×ýW™¶Q/'Þ6ˆ,ÂèÃÐÿ$¡T WÌ)E!oDi°$ŒCY&)¡ù|>¿¹¹¹©¾u hdJ†‘43 ºÝhKË IDATn÷ààà@öûTdŠâ”Ç„ÇÛËÂ4MóæÍ›7EFåz½^‡à{BSXna$úû»¿û»¿ó#0R-!›‡Ïq?¢ºéÉfÞ ¯2rN˜¦i.---Q„L?Uü”cŒ3•J¥Âu²´¬%žŒÐo_øãÿøGj»ì¿I=+›y3H¸ó‡º¨‹ÅbÑðoƒUÆ · ¿CCCC^»Ð/Š=³ö•B¿j©0ÃPœƒT¥Ë0úð6èƒ ãD†‘arOi4 J$QáäédÝÉD D{ÅE_¶üg¯4 ùDƒ†á/T")¸*ŠÅbñôôô4‰±PÅb±èUcT×~Sãÿe5bA!£Õó²‚d³Ùìááá¡lP'5c²Y¡ttt4¨Ê np\…©^2Ô˜aÃPãE‚>8ƒ>ðIB8•c¨I§8}PQ< ãüŒåžÒét:wîܹC}f½^¯«(¹ + Àî}iÝÉ(Þn{FÐåÿ¸¡Â²‰ èDpTìííí©¨ª{{{{–€¯ªoår¹ì¥H M(±1~4býX•ÜâÂí¨Œåö²‚¤Óéôƒˆ.T­V«õÅ_|AýMjöÓA NB¿Îåê%ÉVá2Œ>8ƒ>ðIB8±óÔ>póŠâa)$aÂÂ)¦Þ Ï2¯÷KQ6Éëëëëƒîj ÂgzzzÚiPö·=ƒ²FÂÌAæ×ÛYGoé0yK`•í’-§¦#Õjµzvvv÷>Q­è~4bAÁuëñbgggGäÑét:3333ÿïÿý¿ÿG}î ¹Jöc÷Š ývÅEè·+ßúÙÛÛÛSÑ~nœ-UC͉·õ«DBÜAx$¡†AOÅéógÏžS>ë7†>)ãDóÅËsB>`𦹾¾¾Îmg¾Å{U!;œ²ký‰‚ð ÁàzœÆ` ‚”½Ã-ü‰¢õ“ƒŒ›+ øã/¬ÿð[JM7ÒétúóÏ?ÿ\GW~¨®…B¡pvvvF›‚Âm©V«U‘"Ç4Msnnn®Ýn·9n’ƒH±X,R½aZ­VëÚµkר;5LÚ¯N§3999)«¼â¸ §R©Ôþþþ¾Ì{ýúõë²ùÐ5 Éè§tl}ð£NÂ8€7ÈzWv»Ýî‹/^$å. â•CD4<—( Y? ¹ø½ãºŒðNý%Mø7 hƨ ªÛ·o߯EÇ»¥_$ü[™Ž‡†††&&&&¨ÂÿÞÞÞÞÉÉÉ 'GC&“ÉœœœœT«Õ*õ;vtY~J‡¡ê@âßNì|ø‰¡Ó8 zR,Q¨†lfsÃ, sÔÈxœš HÉ€ßþtù?>ïPËžÄ Š›xܤD>ý‹Å"Eø/•J%?¹T$|ÒQü«]è§d:¶c)ý„Û”ËåòÑÑÑ7îV—õá'Þ}Púÿ>è²û©“„qpÃrgÐ4è %,¥?ü‰¢Õ1¸óN½^¯sâst§X,ãïßLLSRÈçóùÍÍÍMÑçjµZMe"¤Òl6›OžPcçƒF6@ÜÆ“W]áf’UJ"VXŠ×g¬Ð!ëÿ)ÊÌ0Ýÿu„ò^uâÃ0Œ••••V«ÕrûP§Óé\»ví5ëlT¤ÓéôâââbÔíP 'Ž0Id³Ùì½{÷î‰\(F£R©TüþÞ hé?ûì³ÏjµZM…ÐoAÉÏÀ¡P(¨áº­Ë%ú ôAâÞ‡þ‹0ú`ê=*üxFܰ••³âEX·¼|(îÿƒ¨H‰;ïÆ­ÅÌÌÌŒ“€ßh4Q—÷¢Â=pí–O;SSSS:•mÑ%Ž0,ÆÆÆÆ~üãÿ˜’D®Õjµnݺu+¤¦%‚z½^W¡0±Èçóùùùùy¯Ïô—”)ççççóù|^ôûœRaa S: }Pú 2}ÐíÌ“qwÛ8ÄÍz't g¸a7~r;ÄN9@ŠWE˜åÿ,ü®«A_—ÿW°Ýn·————í,•J%ÖÁ° ¸Vír¿}³[Qý J°÷IÊu-L¥R©ú§ú'Šðï•™>îï-.,...z­»F£ÑèŸãV¹ F£Ñpû^*•JQèLt‰·‰¡ã8p’Õ?þü¹sçÎùo™ˆ<ýôW·¹TdÂnü”­‹3œr€ÙêàààÀï{å¨jyÇþ?õz½Þh4–€·˜jÊ&ë·ÜXØè ) ײpñÏx}¦Óétffff¼6Ρ ÷%9D®a"[·nÝò ?]¼9¥Â‚[: }ôA¸}Ð)vÞ‚›Ë ã bxxxøÂ… T=/ ü’“#éÈ„Ý jܺUPô¹_þò—¿É ¦iš?~¬®u4ü*Ö‚PÌé^ñÃîñòNÿ§§§§ãâòo‡ºÉÞ¿ÿ¾ª¾…¡Õ•)i¢#*…išæÜÜÜœ×8r/`ƒæQfØõõõu/%M·Ûí.---¹i}E—.nŸ°àX0чà@ô€ÓÝbç-8Âc\Ç“GµBA~î료gŠ®ó9‰ˆ¼ûtÃ¥à_ÿõ_ÿµ(¹³ªû3·©Ÿê&:(iU(Qý( ˆ+A9*-•t©…ìU öLÓ4oÞ¼y³Ùl6½>Ç}oƒæ¯Kq«Õj=zôè‘袒4^—.Ýâl-8 ²Ð‡à@ô }àä2е¢q ÄÛÑ-ÌNtÏð2ÚPJ£É\¡È!#ÐE·®”9OAUù?n¸°:(%mØJT®‘Øò’e0U[yÃÈ‚K=`K¥R‰šs T*•‚l³Ü‹…·oß¾-þ ƒ¯©W¿4ˆx]¨¨±u²É§¸á1nI?ƒX7Ô¸gô}@Þ†ãY§câ<Ü ¸L‚Ç(ñº³QΡL&“ÙØØØ þ^ËRëÊÆÆÆ×XÖïK§Ó飣££3"/_¾|Ù¯*‹Eê÷ÏÎÎÎú«e³ÙìË—/_R¿ttt$»U%þTFÁml…ÃNIËÍcàW‰Ê5vÚ½¤£ˆÙúÀT¨‰N§Ó9<<< ª*h·Ûí§OŸ>õóŒZ­V£ä¥Èçóù+W®\á<{PJ† ÇÓë³n—6®æÓï!ÅQbQ&ôúàLúÀµâùíç²FµÖÄy¸ð\.—»qãÆ êçƒF4ž¢D~”±( …½½½=ÑçŠÅbqwwwWô9àEK=‚ƒÀïþ£Zþà†ñÌÎÎÎr½r²Ùlvvvv–×2Ü0™öÛá;í²>:Žz‘ˆ‹ûºŸ¦Ñh4¨¥ë¸.wqP ¸‘´LÌ8šO±~\ëÅ’‰>ðAœAøp]`)—­¸÷œ^]]]õsU‰èò-Ê)óèÑ£G^Ii- …Báììì¬X,ûÿfYp!ü‡CµZ­Ê¼ë'Ož<¡x“&¿Þ¹ªå®1.“Éd8ßYXXX*´š»Ë´ß‚kìì?o£àj]¼àÔ ^\\\ôã÷³ŸýìgnµÎ©‰¸”1‘ÝhFƒZ²±Z­V¹š`UñKàm8®¼2¡ͧŠX?jÖ\ J¼(úÀ}p& }à¸CªêG„qàžÓ™L&sxxx¨ƒ€rùöšcÝn·»¾¾¾Ný½ÝÝÝÝ~—ì““““$ämÒË]¿\.—¹ß5MÓäŒs’á ¬ý¨Ÿ•‘æçççÝä¨~ŠÅbQfÎPáîdžaår¹ì¤Lô"›ÍfïÝ»wcìì?o£ @-㹄øq¹ˆQÛ—2&ív»}ÿþýûÜïQïìíííqvÜÙÒDªqÝïG&‹)÷;ª”dí´(A ú úð6Ié'v^U8çç ŒƒL¸^&“Éœœœœø‰GVåò-Ê[@õÑ`ÅØŸžžžŠ²Ó»ëÿ÷ȬA„QÈì?©T*õðáÇ"%@Xa92!Å»»»»ýù ÜÈçóù?ýéOò›ó"0€å"$öT-–H;/"ã····'šˆ jeLr¹\îôôô”3&"-ÖÖÖÖW»'º\X‚L²$2¥‰TáµÑQsdˆÊ$9]й¥•T)ɸ1Ã^ñ¶èƒ<èÃÛ bT•–Å8¨k—Ó#¨9YÔEôß D—oѹ$*M ‚%•J¥ö÷÷÷ÝÆÛ¯w£OPÊ:T™ýÇš7NrBØa9²JÄr¹\ö’£òù|¾×ëõö÷÷÷¹ ”5‰ñ ºò …‚—*Âq³P$¬R“!Å­Œ‰¬€a¸+$duR‚B¡P jUã宕J¥Rkkkk^Ö–t:^[[[sÛÌÜò3pÂtT*ɸ1Ã^Ö&ôAôám±ª,OÜ>x)æ“0†o+¸È…˜¢i6›ÍÛ·oßVß:5ÔJRƒ„luPáÇ~ò8É a‡åøñª0 wE¨Œàoá$+&F`t­ÑîîînW?ñD†ñ½•Ú-;lµZ­rZ£2.ñÿAÇßpØÞÞÞNÊA`iû¡d+öƒÈ]+—ËåîÞ½{×íï;;;;^Ö!·ü ¯•å?¹1Ã^IÑyЇ·AäQ™ .}%³œâBLɼ]¯×ë*J&ÿþ÷¿ÿ}\•)I£Ñh4(•¤ Ù*]A…û1ꂌ·s¬¯¯¯÷ËŠ‰Rp´Fý! ~≼žkÁz©5*U¹A†A6›Í®®®®FÝÃ0ŒV«ÕZYYY‰ºý¨ªË&ëëëë^År¹\îw˲\²¼ø}pgzzz:nJŠKs&“ÉllllPŸ9====4444>>>îuÇ,•J¥¡¡¡!ëüêv»Ý‰‰‰‰!†‡‡‡“Ѝ#¥R©á_ ·`†ªz½^bï1MÓ\ZZZ:===Uýl;Ýn·;333e(Àòòò²›¼“8@Z£N§ÓÙÚÚÚºsçÎ0’jIˆKýúb±X”ÉЯš8ÿ†Á¿Èé­[·n©ÐvŠ´õï‹ ”dÜ0 §¬ÓèƒЇ7 þA¼™žžžV&¢Ð4Ã'…v¢Ýn·GGGGÝzĘëA£Ñh`<èp×…é}¬ê^i'Ìü_ív»=999… T*•¼æâ†ñFkT«Õj*žeš¦9777×þ3ssssA[g©™„ãà*žÏçó››››¢Ï}úé§Ÿ¹@FctttTwáß0⛀ɲtøÑØŠÜ&£Ž³µàÄË¥R©”=¾}Pú0x}xõêիׯ_¿æ¶QÄ ƒˆz½^Š‹7@³Ùl>yòä‰ès2eœ¾X‚?¬þ|¨Þ§aVëv»Ýk×®]S¥ðò. Šv»Ýþàƒ>+À4MsjjjJ¤üJ¤À0Þ¸$ûÕX›¦iÞ¼yó¦]SÔl6›ï¾ûÒIEÍ$,ªw5Ùl6{ïÞ½{"eF­V«}òÉ'ŸŒŽŽŽª¶2X !n‡AœËMOOOs'u:Îøøø¸hcžœœœää‘JIÆ·µ¯iôAèÃàõáààà ˆü-ƒ>T,Wø8xP¼8eœž´Z­ÖÈÈÈP˵»¡Â¸dÑæå°úà'©(…Z­V£†%V`o4Ö####2Âz£Ñh¸½Dk ý€––²_à¡fC:§ 6666D—¢~mœeeð{¹°Ê8Ç×ù™¿Qc%NYŒ¬u@õΠ$®²ÒM¦a/†>¨}¬>FpŠïA.ÖYmôÅVj%ƒL&“yðàÁJN=Vž‹‰‰‰ ]“:Ç j9À¨ŒÓÓÓÓ¢NP La`ÝeúáEîÐG>ŸÏ÷z½žS™¾³³³³^¯×Ëçóy™gW«ÕªÛs-úK¡%™t:þä“O>Qñ>¬’q¢÷tÍû¨ÍßAžo@?ét:}tttD93_¾|ùž`)‹Å¸¬Q[«Õj5ê6Rõ¥Ÿ¤Ë; ¸ ï:m"H&\å971 ‡êŸ êùøA´V xÈôkàýxZp xii¤° V0€öööö‚Ö®ôcÝAÂT¸YTሌÀÅ€A‰ç…0~•ª€0ÜœA/ ¸¥·‚^@ôW) ”UBDTÿ\5(‡ DHŠ”B4Be¢À££££t:ŽºOÀQÀŰ“N§ÓGGGGgz½^/ŸÏç£n/ ÄmoÉf³Ù—/_¾µWǶ <ù|>ßëõzÔ‹\±X,FÝæ0(‹EÎ]…8î-²mv£Z­V£èÀ@ñä¼|ùòe6›ÍFÝæ ©V«U?[(NÄyoQ­°…2>ïDÝ’ÈÆÆÆF&“Ɉ>×ét:““““ív»F»$ŸÝÝÝݽ½½½¨Û@? @1ÕjµZ( ¢ÏAøOT7÷\j¸¬z*…B¡€ý@€BŠÅb±\.—EŸƒð’Ž(ÛÑÑÑQ:NGÝN’Ììììì „˜= *Äåì a²ìíííáý(PD6›Í®®®®Š>áÿm:Ng||||ddd¤Õjµ¢nð‡}zzzšËårnŸËår¹ÓÓÓÓAÉÂ'n{K£Ñh 1h4 Ñ33™Lfrrr2Œöô£ßÃâ¡j†‘J¥RûûûûÖ÷à˜,”(¨Ú$\ô@RI§Óé<Åýûþ¹‰®ToÖÔ’Z¢õÞÉ…2$þX— ŠŒL&“9999IÊå‚zÊ”K7×R§j¼dÞW’ö–z½^·Ú;===Íùîôôôôøøøx§Óéx}îÃ?üÐ_+qÃÚÓ÷÷÷÷S©TÊïóvwwwýîý2¥NeIÊ"(|+ŠÅb‘ªMÊd2™<€Û'HwïÞ½ëeí4 ÿ–ÿ¯¾úê+ÎçU_úÎ;wîüùó穟õêիׯ_¿¶þ¿R©Td.¹@ŠÅbÑï%cuuu b1år¹L†ãçþ`á´òÞÒn·Û÷ïß¿u;z`kvwwwƒx~\ŒZ (œñ¥ÈçóùÍÍÍMÎwr¹\ngggÇÏï ”¸Ó4͹¹¹9?Ö¨/^¼0MÓ¤~þý÷ß_ö·œ˜œœœ¤T6°8888èv»]•múQ,‹*.™L&³°°° ¢Mƒ„åA· ŽÌýAÅ>:ˆp•Ç€x’Ïçóß}÷Ýw"ƒ”_ …B!ny|,…@ÜÚÒ €l6›½wïÞ=‹O¡P(Ä颀”¸Ó4Í›7oÞl6›M?¿õìÙ³gÇÇÇÇÔÏ_¾|ù²JË ×£—Îäã%Ä™¦iNMMMq⯯^½z³<»»»»qHÜ${¸}ûöm¿ûhÒÈf³ÙÙÙÙY·¿w:Îáááa˜m„O>ŸÏ?|øð¡ w q5èZù‡½BŠ”€ïìÅæææ¦î—¼ ¬U¿aF·Ûí¾xñâõóÃÃÃÃ.\¸à÷w-8¸t&Ÿt:^[[[s»llooo7›Í&gÞž?þü¹sçΩmé`a%nÒYɾ±±±Á½?ÔjµZ½^¯Õ¦¸a¹ùžœœœx½ËåååexLlDçqPÄÙ [.—Ëqe )%ÞYD*•JÝ»wï^\c­•¿ÅgŸ}öõ³©T*5666¦âw³ÙlöòåË—©ŸïÿÉÃkþw:ÎÖÖÖ–õÿß|óÍ7áµ Æo/fÕjµÊûoµZ­•••• Ú¤#ý™»ûUÙ0 Ã(•J%(MH>*ä2Y✿§P(U ÀVPëœS@R@WDë áß0øyT%¼páÂ…áááaêç_¼xññÿÉEävüôéÓ§v«#5Š#µèæi'sèt:™™™ì'tZ­VkdddÂ?ÉGtMÜó÷ … ‡i¤eTí ˆ'¢2&AÆàrKñ©JvF‰0QßâÛ<(ˆJRö¦ÔÒ?:Z¬¹] uzË”Rµæã¸·pï[q®€§Ü±Žë6 ¸%¢ƒ€z΄Yà Î%²€Ÿ¤"âC QÜP– nU1ÕOÓ4MNA¼YLÓ4?~üØþoív»½¼¼¼ìõ\ÄxC.—ËݸqãF”mÍ„¤t¬j°dÅKÂx?¤ÓéôÕ«W¯Ê|·ÓétÆÇÇLJà$ï5 øxñâÅK—.]’i‡¤R©ÔâââbÔí²@&iÝ\pã¿øÅ/œ6Ì¡¡¡¡áááá /¬œxj‰¹Ìññññ³gÏžùùM /¢r½^¯÷üùóçýÿ^¯×ëããããN§cÿwëR©T*A´ÆÇüq”¿¿³³³ÃO…BHŽr¹\>;C©+±+Ä=Y˜üó?ÿó?'MθtéÒ¥‹/^ä|ǪÐ3:::ê– ´Ûív'&&&¨Š€T*•º~ýúuN;tCuå,Ý!)d’ö”Ëå2G{„¤€ t»Ýî·ß~ûm”mxüøñcj‰Ï;wîüùó穟Gü² “^qüív»=:::jWšy]B’Ž—ÆŽ“â„C”›b±XDÒ¿ðÉår¹ï¾û }ú”úy¿›òØØØUÃŒøÿd#rÿ7 ”ü Ž5¦Ÿ°/62yƒLÓ4çæææÕ#„ƒuÑ)2™Lfccc#̶yÁMX–$˸Ƞfåq@N,ÿXwL§÷=???ŸϘt:æxxš¦i®¯¯¯ËüÖ³g’ÁÜ| IDATÏž{}FUÎ)Èž•Ax1芫€{xw:Î_þå_þ¥“fizzzš£(—Ëel€¸Ã²ünÊ·øo ¢Ž%tkL?a_ldò!éŸv»ÝžœœœôR Zl«ŽP jN§ó¿ÿû¿ÿkýµZ­&I Õjµzrrrâ¶ÿ¤R©TCŠÅbÑÏ\ðs?£$ V‘sJ%Ýn·ËUxèàÅ® ÎáÝét:““““^Z{®Ip‡ZWÝ0ümÊ\ sýw¨s37kL?a^ldò5’þÉÑn·Û÷ïß¿ïö÷A‹mÕ ªAÍò€ùÃþðû¿çr¹ÜééééÙY2J¥…Uî“¶oc{ ÓÝÝÝ]?ÏòÊÏㆽ<¬hOW‘sJ5‡‡‡‡~òçøÁ©$°Šr‰ª’†:*8‡7Eø·˜žžž®Õj5Ês)±kQ»’që&÷×éå~ßBÖ-ž[WØ"Ijã>g,8›šŸì¬Ü€ItÿNÊœñ %Û0B@¡Ûív¢n‡ܼA†ñ&éß­[·nÔ¤@¤x¤ØVà”À¤xÀìîîîqÎÄb±X<===åT¹zõêU]ß¡uïðê÷~&óû~•Qóúõëׯ^½zåçN‚¼×Ëzwn¹>NOOOÝä*KáãåÁR( ggþe³(8‡w«Õj}ðÁp,~•J¥BUˆ’nmmmq4;ªë!sÝaïß¿ßþ®êõzú.ìÌÎÎÎÊ\ð9ñ˜†‘¼‹YÜçŒwS“uÿå$tªÿž’2güÂÉ‚‡ëiF|&’þE|=¡–ÀäzÀÀ+à{öööö¸‚j­V«MLLLè´ïØ­¾\%ª*,CaT¿wöööö(ïΩòG6›Í~ýõ×_S•X~«‡¼¥àÞ­V«uíÚµk2‹§R©TJ¥R‰òY/7n24ÃPÃJɆmÇM8ªT*Nh„aÈ%õÉçóù+W®\á|Ç4MsiiiI§ Ò/I˜3†Á·ÊºÿrJÌ$5þ?)sÆ/#©sˆI§Óéµµµ5$ý Ÿt:¾zõêÕ¨ÛÞ†êMë×Ðbyåý¥+©T*µ¿¿¿Ï 7²J¯V*•Jmã`YŒ½¬¾aP,‹öŠ ƒˆŸ*FÜÐ7»|ËñêÆÎÎ㩆a؜ûÑh4üjÎêõzªðJ È-9¦*%¶ããããgÏž=súÛ;wîpcT®\¹r…š#Aæbf†±½½½Ä„LI˜3†Á³ÊZÿ8žIŽÿOÊœñà eÇM"2ñŸ¨–N;ƒžô¯Z­Vý mét:ýùçŸ.z÷ÈÍ.ù|>????/úœJ«ŠÀÙ*(9Ñh4££££:ÜSt°öÛ)‹EYwÿF£Ñ°Ê“ê–Ç%¬0VêzïǪHq÷îÝ»ÜóÓ‚#Úù?ÇMizzzšûCNÔëõúÔÔÔ%›±[RÀG=â”yP• ‡ká;888pÛàÛív{yyy™ó¼T*•Z\\\¤|öÆ7¸«Ñh4tÒª$ sÆ0xydúÀM˜Äø‹¤ÌY¨s!h!è ’þÉcÚ,(.ÝV¬(%î¹ÓétÕµxÁMú„@Z.—˃èà„išæÔÔÔ”*ÙźXûíd³Ùìêêê*õó–…%ôëð^ÝàQd¥¿þõ¯-ã9‘J¥R¿úÕ¯~5;;;+ó»Ö3¨òà &¹ *59ÛFÆMÒå·Ôö¢v÷#J4á„è¢`Ï ªº½~ ¾Ã ´ØI˜3ܱå¾Kn›½æ¢Ì<”¡×ëõ‚ª’„9t[˜Š·û3Õó‘›lÒZï2ÉeUΓ8ï-Ü1”E'kpTIUÚ'ª°Ö—Ê9¢Óyòä õ·üºçrÝr©ñÃÝn·»´´´Ä2¼2Žg³Ù,×­dPÜ2“2g8±é\”“Uz\¿“2gd@½ ”dìGu øÈÈÈ7i’þ… ”-À‰¤ç hµZ­‘‘‘‘(î²v(bûEP=·Z­Ö£G…Ñ&?pB£ì˜¦i®¯¯¯Ù¶ I¥R)NÈ®a8”Œ+Ü˯l–nnVn®e´Ùl6····9mZ]]]uì8—³¤•ü‘„9óâÅ‹T…WCÈñ*ö\7’0gd@‰1}IêÄüøõ¯ýk$ýÓžy8U³dHšW@Tåý,7ñ ³è«HÀn‡ãI¶¾¾¾ÅϪyO…+ø[$álâÞÓ£à&é’uÏåºåÊÔä^YYYá&[XXX°ÿ7#eKþ‰HœyöìÙ³ãããcÊg9BnÀAÉ0„9$IN©2n†ÌüøÑ~ô#îwD!kƒF¥R©Œs«yaY?uMÌEõ:µÐ­V{Ôëõºõ>jµZ-ˆß°{è7Ï%Šò~vk?7 *{Â=Õkšš!ß4M“n7jµZM•·ˆ½"ÂÐÐз̻a¼ µ°¾Ï9¸a¾‰Qpë¢; Í8=ÙšÜÝn·;333ùÌÎÎÎÚÅÅÅEŽ2©%ÿ¼Hœáæ j9.ÆIˆÿ·'‹ñJr“„9#JF‹uÑ”±nt:ÎÖÖÖVPmãಖTD{K»ÝnŽŽŽÚ/\!Ï~aT%0S÷D.vŠŒpAÁ²º™TW%a{»mí·ª¸…=u”J¥’*¥‘“57·[¿2¢Ýn·çæææ(ž¾cccc«`çf#æf—U•q“ 7+³¥µå~/ŠÌÝQW°HœáŒ7õùAS×÷lMŒ€að“t]¼xñâ¥K—.Q?ÏuËõ]¯×ë ï•+W®üüç?ÿ9§ä ºþÛIœáä8þüùsçÎ}ŽK‡Ìïý—æ^ä¶Ÿ„9ÃôA¥k£*’æ ro “¸¶ü»ÉÔÔÔ'‘4• ½ŠÅbQ6v;h‚¶öÛ]ÆuÛ«íô{Çû»þÿìÝ?lGŸ?þep% ëB”šh%UQºkè9¢R¢€à“˜4ð¹°Ò€Ür¸ÂL¤"Œ'…%Ä| }." F <ͤÊùD¥‘¨Â,²ç¯ð³ßl6ÜÝùÌÎìÎ,߯.1E~8œÝùÌ?K¤¼–².‘}ýúõëW¯^½rÿÖ™¾¬í{[¢†AÛ¤+N§————Y_O™–+jÚåíÛ·o³.H§Óé¿ýío£ÜD“°ñEXº×™ÓÓÓÓ^¯×cy-ËF€Ô#Ædî>ÏËqÝÙÙÙaý[–Ýûu¯3²ŒÊ^:¨Õjµ(×ÃRxm\«Ù÷Ythœ'jÉØ<0N§÷÷÷÷1³êõzR£ {m¿½O‡¬Ž¨ Ùl6ûôéÓ§:ÏÞ* Y3ÐüöI  ÒyÁþJ dMŸ¥¾¯ÈéR²¦ñÆÕTQQ‚~ã$ÔÊ´ã if”¸UX#(cjgÐo‘„:Ê“ȵºÝgìß"î%¼»yG]Þ:,‰ãÞ¢zܺ^—I^ÀBæ}‰§ ¢]+²ý!»~È8eAD;„Z/ÂÞ¿TY0°–2òû}Y¿³_<,˨×Câf´ÛíöÞÞÞëëY§çR§åŠå90H§Óéܾ}û¶È÷ÔUê %;4Â<555•Éd2,ïå5eI&÷ÍTäÔA{ŠXPV> u’ÅUÒeÓ(§”ªpoá¡kÜQêv»Ý¹¹¹9Ê ~(£ëÎMY‰ž¾ºººšJ¥RW®\¹BÙ”ŒE:N?xðàëÈjµZ­Ê<.ÇÝ»wïÊX‚`ÿöªÞ—©#Ë2GÑ£f/kÑa“S³+—0 ÃxñâÅ Öé¬Ós©ÓrEïŠN=0Èæææ¦Š7£¸è^g(ñí:999Éú`Žbý¿{Äéäää„ÒIöãn˜Q¹º×V”„DË9•T·ãÒxOÈIÕ{K]ãNŠ|>Ÿÿꫯ¾by­jûp8!²ö ðb'ªÊår9ªÏŒC­V«ÉJäˆFiÇØr¹\î—_~ù%îÙŸ¢ìììì$a£SªD&(g£†a,...MŸŸŸŸg}?g.ó è¥Ñh4T¿)EM÷:CÙ h£J'Tƺ¤0kUý8Öñpֽ΀þr¹\îÍ›7otm¼D= @—{‹›®q'‘eYÖ“'Ož°$ÉFCÕ}8 Cþ~NÕjµ*2Q¥g2Våßܺ©±ÍÞ B‡¥\, …B!)ß…U"Ýn·»µµµÅúú é¹ëëëë¬7-™gr·Ûíöæææf˜÷vN%è_gÚívûèèèˆåµ~Rv|÷°uw¢FœäT*•Êd2‘^ê^g 9 …B!î$@¿ßïÿçþçRF“²Ùlv{{{[F<ºÞ[t{Tlooo³Ü§u[f¹»»»k׋Z­Võ¾v}N⨧ÓéLOOOë8ˉ²Ù¸[¹\.Ë\àܹŸoý-—Ëe–ˆ’È€aÆááá!e÷|¯é¹ÔÑe¯‰¦ è4êGþÑ½Î°ŽÆûÅ>6666>>>Îò>½^¯wzzzJ‰1еª¶(Ⱥ׬KBüvÁùâÁ¸uëÖ­ÿ÷ÿwêhÒÕ«W¯Š˜Jªë½E׸GQµZ­²ì ßï÷ûׯ_¿®ë ­J¥RI¥Âï òñ~¼œ³fT]×OÕn·Ûׯ_¿Î»Äž‰¦Jç9LýÕù„ªÄ&(#¢†á=qvvvvfff†õ}dŸÉmüÙºG=B¶ß›îu†²IÈûï¿ÿþ°ÿOYï}tttÄúð³¹"§:×Ù¥Rñ¬UÕ½Î@²Ü¼yófë2뜷¶¶¶( Ét:ÞØØØàýl]ï-ºÆ=ªŠÅb‘u[÷ã•íúÃtÞyŽ÷+•J%‘³d} ¢*šÍfóÖ­[·Â¼‡jkéíý.(ƒ¦*ìM•Ä& ƒÖñ:çvyyy™uC´¨¦åòdëZ­VëÞ½{÷dÆ•:×Êh´×F€”Ø)ëÿ©e·akUUYg§sx9§:MOOOó$yÃv¦y¸Ÿ-¨[¹Ù»Èzkdwww7ìÆ*,Gs»qãÆ ÊL€ ýšT•N§Ó¬Kx #á ê&]îÍϨÓr_¾|ù2Ь¯išæƒPnй\.w÷îÝ»2ãJëÌååååÅÅÅËk½6ôšàÆÓ ¥Yçî­U¥œ),ú<^ë ¨©Ýn·'&&&xFÃDM©gÑét:×®]»æžÂ3 €rŒ˜›Ì{‹–sÃƒÖÆê÷(1MÓ|úôéS–Yºí±ä¬ "g¢P–ª6F¦Ò—Ëåò`@?‹]5Íf³ùî»ï¾fé‡jIÑû5%E¢Ýn·{pppÀúúùùùygÇ€:-7ª3¹?~ü˜gZV\ÓCu¢s¡Ä>l$š² Ïúÿ³³³3¯N{­jPƒÀ} eÄ"›ÍfONNND=¬u®3"ñÔ ðW©T*Ô$@T³üÖ97›Íæ£GQÞ/—ËåVVVVxbyoaÅûvÒ5îQÂZ^^É0Õëõºèý&Ü‚–ªÚ3X’¶LŹD@ô`CTì©óa–c …‚*{†¸ýš@#î v‚8+¬ûè?Q]è*™}Öß%ŽiU:×Jýp—-å{óÔ#çhïïJýmX…Mè\g‚°Æ§kcG4»q-²Ì(ï9ðÕgê³%èæ¹VyŸO"î-¬×DÐ÷Ñ5n±Ì|P‰èÎSض#O¼^ŸéW¿(ÏC‘£í²ë‡ŒkŠzå‰2»ÒmØó³¨™Ôß×ù¹,Ï_¿g9ëwöû®¬×/徑è†A_ggOÏ¥NË¥lˆÆ‹²Í –Ó¹Îø(¹¹§û/,,,°NäÙ„ÎÎ*«¸VÕÎÚó6ºt®3 >êÆ¯QÌÚt´Ýn·÷ööö(ïÉ; Ê{ e3¸ ºÆ zpvÞ¨ò±p+9lýþóçÏŸ»§’—J¥’ЧQ8¯ÅR©TýþöNùA=Õ„Y ó˜Wªn·Û¥œRäµOV’$>`´)³öô\Ê´Ü~¿ß§¬/áÅzþl,¦k¡¬Gwßà>üðÃY?‡râ€Nvvvvx“ºÖP_»ÝnonnnRþ&ʽ¼<|øð!u3à U^"’ðqÐ5n ¹™Ÿ—R©TJ¥‚•ìv»Ý;wîÜé÷û}6úÛÝÝÝM¥ÂƒèŹy JÓä½ðì¦os/yÔ…×>YI2 €aYH/öÚhÊ®ÜQœÉÍzþ,‹t:~òäÉ/ʨèZg(YN÷F'¬v:Îááá!oŒªãí€èZgD¹¸¸¸¸¼¼¼Œ;ޤ¢Ô/ÈçD7ÞY*ÎR£l§]ãv²6ósrîIAéÄ7›Íf&“Éè¸ÑŸìY†ñvÐa0ÐcóÀÕÕÕUjÀëä#ˆßH$¨›týùÏþ3eZ®ì3¹óù|þæÍ›7E¾§JSsT¤sa‰vBÙPÕŽž×‘j6Öx˜iȺÖPµ~†¾³ÖÖÖÖTKPëºyž®qC0{½¼¬ÍüœGM&m³>*Ù³tÙvçnrrr’5É÷úÿ°£•J¥4µÓë¦k=ðt¦U˜pïÞ½{ÔQ#UfäóùüW_}õ•ûÿ·Z­–è™y"é7üžìÍü CÑ~jû‚7Y/‚½¼!•J¥xÖdzpnÇ©XÃPÚ˜†¡Æzú©©©©L&“‰3ÕŒLÀ0䜟-óLnꮽF£ñå—_~IÙ, KüéVg ƒ¶€½A ëC´ßï÷);©ŠÖn·Ûï½÷Þ{a;Ã[[[[22÷†¡g}è8  Ûív©Xªðl²,ËzòäÉwrTõ3ßu~¯Z­VemægúŒö{áMÖ‹¶ººº*sV€aF¹\.«0#€º£¾ 6666(תK]E©Àááá¡Èio27C£îÚëÜ!œºYT¡P(¨’YTNuÆéçŸþ™åu™L&3555ÅúU}#:QÂt¸u­3a±Ö9G×YÔç’aÄ·×É;››››*oh¦kÜ ç~9ª­í§,a4 õv™bV@Üt[OïµÊÏ(,i©OÃÉÌ3¹©Gþ=zô葽åY£ƒ£‡Ó©Î8±®EO§Óé?þñœŸŸŸgyߤÜý²Áa;ܺÖÐe¯ ÃÐw@œq{¼S«Õj*e¦kÜ _­V«±ì—§ÓÓÓÓ^¯×c}½jû…8E1+ ”=£âV­V«<ËfFa@c¤†Ao8y‘y&75[Õét:>|èüÔµÈét:ýàÁƒqO-R‘uÆò]\\\d]•„©èA×—ˆQ2ëÌ0”LÜ{CŒž½&âM7 }fx-¿k4J¥R‰2 ]㎂só6'Ê®îv: ¢’5ÎÑ~êÀååååÅÅÅåoÊårYåY¬ÎY,›GÁ>:’º¸×ò"?QO§wî™Á³Qfœ›.GiäÔÍ+¼Èš íµq¯ËíÛ·oS¦"«zþrÜT¯3ôÛíöÑÑÑËkÿøÇ?þ‘åf÷ú‚®¯F£ÑÑðұ΄‘„º¡ê>˜ÀÆkù]§Óéܾ}ûvTqPéwÜXI¬Õj5:Ð6Fû‡á9îÔ0Þ&z½^/î$gJ¥RI¥R©éééiNÙÙÙÙ±O•¨×ëu¯×Ù ƒ“““ÊìdÃ7s´P((§† ƒAØ=3tiw…5r žÈ02ŽBã9ò¯Õjµž?þ|Ø¿ñLEÆR€ßS¹Îø=…©×ëõNOOOE¾g”òù|þÙ³gϼ®¯V«ÕºqãÆ Ÿ¥k}è: `www—º66ª#kMÓ4Ÿ>}úÔÝÀí÷ûýëׯ_Wµ#¥kÜq«×ëu–Ù–ºÌ Ðm´ß ï ºt:Þßßß§tý:½2µÛíöÄÄÄ„J³ü:ÛoÞ¼yC9‘ÌI·™££Òî¹€a„ߤKÖôê‘ý~¿çÎ;~•züR:N?yòä‰J›ª¨@Õ:ãGÔ4t›Îkу:ÿ2vÇֱθ±ž«{rHW:Î0 ¾S86666d/Qó vî±£"]㎓×^ n"òè:ÚïEÔ :]¨6+@$]61¶ [RT#™ LFÆ™Üù|>O=›—%]_RŠu&u3 ºnŠR,‹~SÂ:NgaaaAtÃIÇ:ÃkŽÌQ‘®³xâÎår¹•••I!yvUýÕ5î8±±ÜjµZüñÇ*޶Z­Ö•+W®è>Ú?Œ¨tºQqV@X{{{{:%¥F餔‘LF¸sÀEOgáÙTƒ²®Oå)—:Q©Î°àÙLÇ‹ £Ñ<‚v€•Õù·éVgx%åtQë füžW^õÑ_]ãŽë>K2f…‰P*•J©T*5777§Zl"ñlš$I˜ Ûhú¨”2² Þ)F2¦³Pü3 zV§±uÿþýûX ð+•ê ÞÍt†ÑqŠw½^¯ûòÈîü†~uÆmjjjŠå„]g‡$.;ë»ñνQ­Wže‰]œt;NAKÁlªíàí•J·Ûí^»víš®_Qœ³J¥R)îxX©v Ñm“O©Z­V©»KŠÞ0¤X,©1œŸŸŸótÌUø¾ºÓ­ yê—jßÊÞ­ÖïûGuä¥nuÆÉyœŽÌúU½^¯³þμ÷r7žëÜ]Ǩï!â7g­_N¢ÊÌ0üï,ßϲ,ëüüüÜ+VY÷]ãVAPÓa'ùQÂs PåYË#è:vs^×,í¤0¼îCÔ˜ec¹ÞYžé~Ï%ÖïìWYŸÏh‹1¢VDÑÞ ÷æý<•ÏWZÜuFv¼º×–ÆBÔ|ÝêŒËCG¥xUG€§1çþÝâH­¼l¢îG²Án¢®]ãVEØ DKf}G@¬ {…J ÖçHR#»À0è›t‰<ÒëØž ~Çþi·ÛíÍÍÍMêßáhÀ_ÅYgxˆÚàÇüQD<2mögo§y­®®®F—nuÆéÃ?ü0è5*Å;ªx6ÍRe/€Û·oߦNó][[[Ãò4àÕív»ssssFm °.ìßkiiiIäÉF V£Ñhd2™ŒÊ›'yóLª‘N˜¦iNNNN²¾^äÙ^Çöø±¶g½(ŽüUœu†G·Ûíž…yUÖ£û©×ëu¿Íþúý~iii)޾nu† ªA×½Úív{oooò78©DX]]]µ×Uc °úšÍf3“ÉdtßOvREÄiF£‘J¥RQ®°°7Í´%}óL`DY[”´)pÀuF-,S«D®æ¡sa™ú†)³ +]§Òë7¨uI–.ËBÁÛ?Å@œ–———YÞÓéLnuFÅb±è7êoo3Óqg¥u­3,3t˜Áân/AtF6`Y–µ¶¶¶ÆúzÎä9PgÔQ¯×ë…B¡à÷šR©TŠ{=§Îufllll|||Üï5GGGGºó#Œr4WÜSˆA ¨3ñÓaÊ¿“JufØR¿ÏdYº€éÿ0Êt=NO׸¸Q¡Ðù˜u&~Q#¢¬R Z×7l=_б3è$À¨Óµ#­kÜ ÆHž°½½½Ízü^¿ßïSW‚äA*UêLµZ­-—vÔgЀ[[[[ØM@/#—`i ;áŒk@*UêŒišæâââbÐëÒétzyyy™õïZ­VëùóçÏEÅ Ñ™MYv £\£ u¨T«3³³³³3333¢ÿu@O‰H˜¦i~÷Ýwßår¹œÈ÷Å(Wr¡Î 㯧 IDATÕ(Õ™ ¯£ F#îÓ€ÏÈ- À(P¡Î•Ì:szzzÚëõz,¯}ÿý÷ß7Œ·³¼–/´Z­Ö7n €Æ4Móøøø8xŸmvØ7ÙPg€J×:t+•ŽW€&ºaÞëõzî±!YPg€J×:“Ïçó½^¯&Vtþ@¢æÅb±÷w¹Pg€Jç:S,‹¼qÖëõzTqÙ0¯V«Õ¸¿ȇ:Tº×êLŒú€’D5Ì1Š;:Pg€*)uƲ,ëüüüÜ+>$´@iaæØ¼mô Îê èn¤¬ÕjµT*•š›››ÃÑmÀu¨Pg¸˜¦iT«ÕjÜñRÕëõ:Ëw ƒãããcÓ4MÖ÷.‹EÖ÷îõz½|>Ÿ—ù]ŸÏ÷z½žÌο’¨×ëuQß$bé4ûô²ŒJGÑɦtZÝT_ @ùn¢¾KŸ |XÛn^í6ÖÁ¦b±XŒú»ÞÞ‰;H¦ííííl6›åùÛ›7oÞTy)ëw«ÕjµJ¥Rñ™ív»½°°°Ðét:A¯-—Ëe< €²Ï` æ,<Ê^;ÁIõå3D|kùS7åÅÚŽÖ)€% DМ[·nÝj6›ÍaÿvãÆ­V«åõ·…B¡W§$ŸÏçoÞ¼yÓï5QLû÷Òl6›Ÿ|òÉ'ý~¿ïõšl6›}úôéSLmãW­V«;;;;qÇáÇnhìïï¾¶³³³3 tÍf³ùÑG}4­¾\.—UJT*• Ë2Vý~¿¿´´´”J¥Rq=ËUðøñãǹ\.ç÷š~¿ßÿüóÏ?÷k³Ù¾úꫯDvXÂ,M R­Î«àÓO?ý”õµ¦iš‹‹‹‹2ã1MÓœœœœöoF£‘ú‡éééiÖ¥C^ŽŽŽŽÚív;Ì{øÒq@Ðg²ÜDƒ2ÏqŒb³dnU-`ÉãaÆ'ê@ž,¯¬±&F]’gØXG~UJ Š˜ŽûÛ¯xÚŽ,#ªíÅò»aÂÖù$Í \/agj°Üϼ>ëÎñŽÎSÚd:ÍÅè–ºÈEîäug[õ¥ n,7\•p:ˆjÚ¿å&/zÊæ0XB£l”ïªÊ3$ÌÔ_$Ëå9çUf¬'?…isFñœó¶Ó•¤@”á0 ¿¿å©K”{0‚.$žrÐÍ.ªÆGÐMRµÎ¿-¨üЙcÕZC7Ö›|”"ö+Ñm…  Okê¿[RGaïg,‰í'–ºâUß(¿•×5Õ  ¥?üáˆú3MÓ4ß{ï½÷d~ÆçŸþy’;Èï½÷Þ{Iþ~"„iòk8…91ÃW§É£áÂNóubÀ0‚ñ²£2œªÎà‹RÐ=‹·>½oT;É{%zxëURž}aïg, 4³'GåY‰€"ÂŒ8P–ÃnþA7ù8ö%pBãù÷€a¨×ªN£Œ¦išŸþùç¢ß“wäðË/¿ü’rßõk1®€¿üå/a}-ï³8è·åí êt}z ›ˆj€bT®O$ÁÛÁæm¸½~7ù8—&°Ä7ª€a’œ…Rfîd$åoqSOG’õË“HÂ,€(:ç"fïŒÊ³2ÎÀ;¢Þ@w¦iš”¿±ÏÞÝÝÝåùÌn·Û›››«Õjµ ×Þ½{÷nÐ9Ͳ¥ÓéôƒèÞùêõzÝÙh®V«Ut¼ÀÏóçÏŸû1ßét:ÓÓÓÓ©T*µºººêü·ÝÝÝ]–û¨a¼½=yòä öPÏ9ò­V«uïÞ½{,¯m6›ÍG=¢¼ÿÌÌÌÌììì,åotuqqqÑï÷ûÃþmqqqÑo‰ãüüü¼Üè@$þavvvvfff†ò7=zÔl6›a?»R©TJ¥RÉëß-˲ÖÖÖÖxÞÛNR¤\Fƒçýr¹\neee…ço!¹Ü3T …BÁýšt:Þßßß·_ƒQopêv»Ý­­­-÷ÿ/•J¥T*•š˜˜˜h·Ûm¯¿¿wïÞ=¿‚S6›Ínooo‡‰äàyommmu»Ý.ëë>|ø°ÓétX_ŸN§ÓËËËË”˜tÕét:½^¯7ì߯ÇÇÇÇÆÆÆ†ýÛÔÔÔT&“É û·×ÿ 2Nà7’ ÊÔU¦¡ æh æÑ¶¼¼¼œN§Ó¬¯§Œ8°ØÝÝÝõJ,,,,d³Ù,õ=kµZ-“Éd†%)VWWW§§§§) ÛÆÆÆf€óþóæÍ›7Ô*;;;;¸?©ƒu½Ì½@ìYF£a'+YgXu»Ýî;wîx^º …B”õN…ò¥ˆ+^žgñóçÏŸS>£Ýn·÷ööö(ã7úÍCÕúpvvvvttt4ìß²ÙlvaaaaØ¿ùýnN§Ãz]Š¢jùB„D¬I‰úøÄŒ˜U™òù"§üR×ÀS¾#Ïúú¨F/y×þ³6ly6[ÂTî_Ú”µÖî%¬—„™.{„}ž¨x´žJûèV¾*ÄK÷ÞJÝ @D]‰³|YïÁõz½î÷ÚaåÔNùâ‹/¾`mÇ„yVªPYa@Idm˜%óG@̈Y‡˜© [QêÚ҉ ¾w” ž ‹¨(žÍ“Ò¡ k¼›Gñ&¼ÞC'ª'Dµ§Z"€òýdÔ1ÝÊW•xyÒ¢6á•ùY*”/%àwÿv½øýn½^¯÷ÙgŸ}&3 BùR! AGe‰~` fĬKÌÔ´¨òÙ Ž+©Á‚ÚqâyPð4„t혉–Ô€ˆ•ˆ€“N U²Ÿ'ªÌÞ ÞÓDÅ­[ùªoÔÉxÙ³ T*_J ¨Cï¾û}Ïóóóóù—ù •Ê— ÁDg¢øA3bÖ-fJ\¢2©2?“Ú9Qyú?ï‹ú Åé­¤$dÌ2pSyA2~ƒadž±ÔÕããããÏ>ûì3Jg2ìo [ùª/õº ›ˆ¦–ŒwX¬åKI]cîkÅï½YÞωõY©ZùR! H\gd‡ùQ3bÖ5樷"gD™TñÃÒHÚtÈ zŬÓ4ͯ¿þúkQŸ£s€§~QÈN8©V¾ª%¢¾ï‰n4ó,…Š2fÝÊWÕxevȇ¡^§¬ßCÅò¥$‚^ï,÷ ¶CµZ­ŠN¨X¾Tq&u ÀãÇÇqFv:N?{öìσ˜Ù!æh°Æ|xxxHÙ=þÓO?ý4L\~ÇË sppp0ìH UΩí÷ûýaÇ]¹Q¿w§ÓéòÄÔív»”¿aù] ¬¿×ær¹Ü›7oÞÈzàZ–eýôÓO?ÍÍÍ͉|_8;R''''<§K¨¨\.—íz¦Ê4tUÔëõú°ceÊf³ÙÃÃÃðװ¤Úßßß§ì Ï#›Íf×××ש§[ùªïûï¿ÿ>å}þùçŸÃÄuvvv&zwz•Ë—âÇüÑëß&'''íäîØØØØøøøø°×õûýþ‹/^ˆŠÉ0’S¾qJL Z­V£® Nét:ýàÁƒ”%ÄL‡˜£ÁóååååÅÅÅë{:<(Çù=pÖ×××Uèð¼~ýúõ«W¯^½nrrr’Òè=:::ò;';ˆß¿ßÕîhuú½d³ÙìÉÉÉÉ` ft·Z­V“Ôá¥p&a¢èHÅ­P(ìï;ê{U‹Åb\Ï“l6›}úôéSÞòãš½yóæMJÒ^·òU9^Ó4ÍÉÉÉIÊ{RŸYn§§§§^gÞ“Éd2SSSS^ÿ®rùRù ôÌÌÌÌÌÎÎΆÿ1Ž^¯wzzz*"ÃHVùBHQL cÅ:ꀘÃAÌÑŠ9ªéKÔ©é^ þ¸–U Ã:Ý—:2lG™:%|Ø(½ÌræÙvM‰ìªº ªõ‘n,37âZ†åNõ*,PåyB­ûqß«u{^³–¯êñF¹äMÆgª^¾Ô%,Sû Ãÿ~Îú^,ñ«^¾TØ „¸n,?b1G#(fê͘÷AM}@{Ýœ£8M¥Bí(…m Që û»Dù€öëÄ}(צ‹To⼄iÉÞ‡À‹ì• Uö;¡”µ ÏAÖxu+_Õã¥Þ D\Ã"÷¾Q½|© € ïIJ¹Ÿýl‘P½|©°@wï޽˳Nºßï÷—–––R¦§§§)ë›mét:½±±±˜ó(ÄüêÕ«W¯_¿~Íú~¼ûøM/só›þvQööööX¦éS§CöûýþÙÙÙY˜ØºÝn—òî锥a9÷ p{óæÍ›8öЈš³Áåwît:éééiûS©T*¼ïÕn·Ûö{ÕjµšÈX½¤Óéôþþþ¾]gT>Q€G˜©²­V«uåÊ•+îgI©T*ñ¼ËóÏ×þ7Nét:½¼¼¼ì÷ÝÊW‡x©{ÞÄåÃ?üÐýÿt(_ß|óÍ7^ÿ6???ÿ§?ýéO3333^¯ »DÖÔò<£Ôé<™A¿, bFÌIŠÙ0hLÞé·”¬¯×¨°*SÇdŽ„ÅqÜ¢ûûè2š®û €8bŽ£ƒÇìQËCâœÀ;Š.s‡s–{ŸJ×¢_YèV¾ºÄK}NÇuܲ{„Z—òå™à÷›ôz½Þ×_ý5Ë÷ 3@—ò¥Â N”QAÃx›úàƒ> lÕív»ssssF£Áú7~™cÄ<bÖ3fÃ0Œ/^¼`ÝA7hóœa¨£à^»ÿG92íçåË—/›Íf“åµ~;ësqqqqyyyÉÝ[”]•ÓétšºièÁ=j²»»»u îÙ¼#6£fvvvÖoTnÊ3euuu•:S#èY’Ïçó7oÞ¼IyO™²ÙlvaaaaØ¿éV¾ºÅËJôs¬Ü'$µ| æg:NÿùÏþ³×ߊj“$¹|ã¢u€2·Óét®]»vmXÇ€Å7n´Z­ëë‡ fˆ™*1mÏ “rã÷›þÿí·ß~ëµD‚rãZnduuu•õ³¢ä®Ûssss¼÷Yvwwwíø®\¹r…r%Ô„'Ï3¥R©T(ÉdÃ0Œµµµ5¯¹ ’´N^ÏeÝÊW—x©§Þ¨B—òåA]èä5 C•äòK` hzB»š¦i~ýõ×_;; Ô³¼7777ÃÕív»,gvÛœÇcØs0ÄÌF…˜ïE9;Þ/™0 ¥Aàw´Þßÿþ÷¿{ýåÜá¸F¢$jÍÞ0¥R©ä— ‰jý÷ÙÙÙ™jÛ84†]ö™L&Ã:;EöŒ&Ìø-Ó4ÍÅÅÅEÊß°îKâ¶µµµE9CÝkX>ŸÏ_½zõ*õómµZ­æuO¡6êæççç‡r¢Sùê¯nF¡|ýöð#¢-1 凡 纵r¹\ö{Ù›èX–eýôÓO?ÍÍÍÍ9ÿ?eºt§Óé†Åï|øç³šÍfóåË—/Y_ï5 Œw‰–Ýñ÷Û„ruuu•wÆÈ°¾nå«[¼º…ò=;;;£tŒ C̦Ć1å‡ß$ìŽÿÎÎÎïîììì |g5»U«ÕêÉÉÉɰŽ¥Sptttf´ÔvyyyyqqqÁúz÷.¡ˆ™ bö§JÌN”e~ë*ݨ3#x³Ô ‡½†œ·“i'––––¨¿xâXË®ŠQ˜Áb£ìe‘$ÔiÔaŸ)ÔQ3÷,0ž>Ãx›È£œ>aÏ¡$‡5ðu+_ÝâÕÍ(”ïéééi¯×ëQþÆoF&Å(”oÞ1Œ_wW Óñw+ …Á€o7];á5û€º)˜¨FuŒsZ1bf‡˜ý©ó°÷¢,`MxPŽ53Ä(•J%QkÈ›Íf3“Édx×|Û×´G-›ÍfONNNìY{^ç-ëȽ[uРƤòKÖ6qJí¸gñlðU«Õj¼‰<ê´d÷³O·òÕ-^ÝŒBù¶ÛíöÑÑÑåoDÍÈ…òÃ;ù|>ÿË/¿ü"ëÌUç9Í~ÉçA‰êÎØår¹ÌzÌBÊ”““““ö÷Ë9I1CiT½—2-4LÖ—’T‰rÍu–† ìóáeŒ²;×|%œçÔ£ãïÍy?‰bOÑœÇ7íïïï븘h”=MD$N©÷=”:ý¿ÕjµîÝ»w§²üÍ0~ÿ¼Ò­|u‹W7£R¾ÔÁ'Q32G¥|£öN”Gc9“noÞ¼yÚ„ Œ ª1G1LJҨòÛTÐFJyظÏ|¥Œº÷=a!bIT”(w÷:¿Z­V±D%ˆ{8·‰‰‰ Ö8œû𘞞žf©û~•± |'b½uîQDMbŽꌲ8Žu£4ð ãíÝ0 ½0‰UÝÊW·xyÖšÇI·ò ƒ²×“¨õÿ£T¾QÓú@Õé8-1G#‰1S²¦,gÇSfFP²¾aw›¦ê÷û}ʉ NaŽßË^Êg½É/ çìÀÁ ÜE£€:£LÊ 0çýŸÚÀÕ± p>ût+_Ý⥊kôÕî &½|(SãE­ÿ¥òš– Ï EÌÑ@Ìñ¢ŒÂí@92ý?곦_¾|ù2ª£ÕD%–p,¢úœ³¢ÚíÞÞäw0àÛ߇•s”Ÿ2;0 ç飴d$î“S¨ |‹(«q—/•nñŠ@MB…¡sùRyâúž:—oÔ´L€š(ˆ¯ìD95ñ@=U ¬0£ÿ6U÷'°Q§ÜU«Õªßr ™K›išæ×_ýµŽ;÷cs–JÉ;:Ñͽ¤/Ìì˲¬óóóó¨GùûDd2™LT‰: oìe‹£¯ã_Þò‹/ÏnóLT}`mŒÂz'Ý®7Ã`H”J¥’ߚĨÈꨒ!ökTQ2ò”éÿëëë묳 D1úOYw'u4„åÞìÑ ÚoÁÙ±×9;€åws&‚¼Žû•Á>"Š=@]£ÖAI*Ó¯y¦™SO’H –öH¿ßï¿xñâET1Ÿ¡ €F£Ñ°A;9;²’¥R©Ä{Ž4D‹u4~ØùÊ6ʱP¬Óÿ-˲ÖÖÖÖXÞS£ÿ†Añ+SVÔÆßïmwüyGtGÖ‰8®®Z­V£ìlÆmwww×~>/---E•HrþnÎÎN”Gô9Û4£4½@7:œ|Ç>ª°äõÆl*=ü&`gÆy;Ûv2@TCÃŽGÆqR eÀââââ°iØ”ÓIXGrtý7 zƒˆº£¶åT ¯†½q›È©ÜöquèFÇòý €R©T•·Aç4{±;þ^ñèvLˆa æ¨ æøQ–xmZÇÚ8£L5ûöÛo¿µœÉ¹a˜QŽn·Û=888`}½ûÌj*Jòe؆\ù|>ÿË/¿ü"kã6ç¿d€s÷xìÿ[ív»=111‘J¥R¼Ïi9—,V*•JÜñè ìýb„ÙèT·òU!^êòŒ°ÉêFÌ£Ttƒòe÷޽ ŽŒQvç9ÍA çf<¢§èíc K˜õ…ˆ1ë3ë2€l6›]XXXpþ?ÊF}”¡ÿþ÷¿ÿÝïߩǾ„jOAÙ`ffffvvv–çsLÓ4Y_pppà¾WSa¹7¢sŠj÷xÝ9ŸÓ©Tt' ˆàNÄa¶`|S¨)£°a¦P‹hà‡Ùõ]·òÕ-^আ­ÔÎou,_ |åy§V«Õ¢ØÇÝÈÓ¹ îªÂ9ˆY ”Y îÝþ)Óñ†u@yQgbD9…ôÕ«W¯^¿~ýšåµa’”½°ÑO2Åq¢vî÷GBÇÑ¡NÔ¾ˆx©ûœ8;|º•¯nñýY6fj¬Îou,_ |åÑòÀ¨×ÄŠ€˜£˜Õ@é°º³÷¬#È¢; ”˜ #øC‘ºÝn—²¡ ×Þ A(£÷¢ö8uÅu¢€vî§¡L¡6 ‹Šz¼ªs 5µ/âH>êšo÷I':•¯aè/uP$LÌ<³AÜ3u+_Ý |ßqâ…“– êC…5!ˆ9ˆY ”uëÎ)ë”)è”éÿ,¨¿C6›Í®¯¯¯‹úü ÏŸ?κV;—ËåVVVV(ïO9%AÔ  ¨OÀÎýü¨S¨Ý³°¨¨j爺aÐø"–_Q—)¹ãÓ­|u‹—²wf$eÖ›a Ÿî­[ùê&ÉåçæZ& ƒöÀ³&V$Ä ĬÖuëά&ej¦Èéÿ6Ö½ lkkkk¢güéOúS>ŸÏ»ÿ?uÀÆÆÆ%QD9%!ÌèÐQM;wê;Y' `ç~1¨S¨ÃÎfÚØØØ t¨Ý÷nj#˜w†“aÐ÷91Œßǧ[ùê¯aÐ7ŒbÖ›a xб|u‚òý•K€¥Ëçóù^¯×¶ùÓ0õz½Ž˜3bŽŽsvÖïcפ×ëõ†u’ò,Ë:???gýDÿÅb±ôÝêõz5¶jµZý½ƒâsžõ¶Œ†½—Åb±ÈÖòcý=t5ìšwžÔ0ì÷ý[¨„õ~&º,(÷Þ°u“úìvÿà¹ïò–5ÞóóóswgB·òÕ-^ž÷ èuBÔó^§òe}¦ÊhcRÊÉY>:•¯ìÏã99q¢¬–eYÿû¿ÿû¿Ã*bFÌIŠ™ëÃÆnd±vneÞy:"îöç=PDvÖm"“ Îòõ;ñ4‡ÁÃ3Zõz½îNÈJÞ©(®€aÐïc¼¿ åÞ1ˆëPó\Ë<ŸãÕÒ©|uŒ7Š:!òY¯Kù꘠Äm‹»þ²¢&¡Féùé‹çâ•Õir>èý~ ÄŒ˜“sÖŽ[¯×ë}öÙgŸñ>Dâílò6ÞÝ–ò¦t,‚E”zÇÒ(°ßOÆÈ.O£¥ dˆ3Às‹¢åuïæy/j'Ed‡O·òÕ-^ï'³Ÿ¥ ¼gP§"Lý9³ è=u-_Ýâ óû…}æ±¾Ÿnå«k@—òåÁ[ÇG~9@؆º ²²š2!æh$1æ ¢ÖoÛ¢š1È  IDATþWg…ZÞ, š0¡¤wþ CŸŽ)ÑHJ€wÔL–ûšJÏ?–ûžnå«[¼*Õ –WÊWç€åËCtÙóÙ¦í1€¶v»ÝÞÛÛÛ‹; Ä Ĭ†ÓÓÓÓ^¯×õ~Ô£úxÝ»wï^«ÕjEñY^X:…B¡ 3†l6›=999a}¨€ºÝn÷Î;w(GhÉòèÑ£GAG‡¶ÛíöæææfT1ùÙÛÛÛk·Ûm¿×èV¾ºÅkj´‰:Nçáǃ^§cùê$©åûêÕ«W¯_¿~-â½(´O†aT*•J£ÑhÄbŽbŽ_»Ýn‰x¯N§Ó9<<<ñ^AºÝn÷Úµk×Dqµf³Ù¼uëÖ­8chµZ­{÷îÝcyíîîînÜÏ¿F£Ñ¨T*–×êV¾ºÅkñ'ã7777ƒ’A6ËW'I,ßn·ÛÝÚÚÚõ~¬‘0 øqãÆ¸Gë¨s4süDÚ±>ˆEh·Ûíëׯ_W!ã À#ÎNu§Óé\»víZ·Ûí²þMœÏ¿N§Ó¹}ûömÊßèV¾ºÅg2¾Ñh4vwww)£[ùê&‰åÇwJL Ûív?þøãuê4!æh æøŠxxG5ýß©Ùl6?ùä“O]­®®®FÝÀìt:………jÒ6®o¼†¡Wù†~ñÆ‘ŒoµZ­7nÜàù[ÝÊW7I,ßÛ·oߎòž—˜€a¼}hÌÍÍÍÅ=}Œ1G1ÇKÄ2€(§ÿ»5›ÍæG}ô–€®VWWWkµZ-ŠÏ ÛXn·Ûí>øàƒ¨’à"÷:•¯aèo”ÉøV«Õúøã?3Ò«[ùê&iåu’+Q ÛêêêêÒÒÒ’N#vˆ9ˆ9>aGþïÖn·ÛIHÈÀhªT*•R©T’ùF£1111ö~U\T¼†¡Wù†~ñF‘Œ¯Õjµ¹¹¹9Ó¼u+_Ý$­|›ÍfóÝwß}7)³cõ±C"ŽÎ@̈9I1ÛÂç#úˆ¬0dMä>Z&̵N¢ÏDvÓý]ާÃ1€ÑHÊ1€AD]ÿ6™çR˸çÊ>G[§òÕ1^Y÷Yס å«ó1€AT(_‘d¨{[‹ÌseÝ,3bNR̆ÁƒVù,z¯ï‡@4té˜"QIØÂÞ¢¼?‹8 <êç‰Nå«c¼2î+<PVq–o’6Ýê/ Q PÝÛjBðt tÌ!fĬjÌIDùd60x°&-dtx°6öUiøêV¾I—EÏhÝÊW7(_€Ä3P‚D=€†x’ÐÏ2$4ijq ’¢î“4ÅšˆëˆN èlzÏ„–eYççççXópÎã±Ö Áòù|þûï¿ÿÞ4M3îXè,˲ÎÏÏÏDÅb±wì^ªÕj•õ{œŸŸŸ[–eÅsÔPFõz½T‡ëõz=è}¼î…,Ë ÷_Ü[t ÃošÏçó½^¯'úš ºFeÞ@1¬ ·jµZö÷A«°-V,çaz½^/ŸÏçeÆÆ£X,Y¿Ã¨?¸Yëà`0›¦iƳ.’rPåþtÏò*s¯ß(êø)ß%.¸ÿ&—Ï‹ ©×rÐû¡Œ˜$4ð)YýaTëRFÓd6êu‚2“# ÷ÕñÞ¿¼í^qYõ÷_пqPеÓô>ª]Ýø”‘?*eÀYGÓÐøü-Öº¬ê¨£Št¿?¨Žwê¼WÙùÝe”1î¿ +žAɵ Î{Ðßcé ÀˆÒ¹ÏúÙûÛßþæ;F‰u4M…XUÄZŸÑða£óýAaGÏYÉèÄàþ ºSýyaš¦y|||Ì[P" ‰pv^÷Tm梁6ðYã¶?—åA*#N Ö²DãÓkÝPiÔQUºÞtÁ»vžJô4_Ü!)T^°Äçl€êp­è€:KS«öŽ |–œWFFÕ¬8µA þXAZ]¬1Ðñþ  ¿²uvØE/ ÷_HÕŸ¬÷Ñ/¿üòK]fÛ¨Œw‰£6ûªèØÀOâº8–‘@4>iÂtTà-ïºð*¯û*#Ü!‰T^$q¿ …]ž©Å2cÝøIÜ—åÁŽÆ'–F6ÙºèvЉWÙøÝÃXG*m¢;,¸ÿB’©þ¼HÚ‰ª•lUþ~«S?‰gã²”Ÿá°4ê0*2œN÷Ýx• Ë}Œå%ºóû/ŒÕŸ¼û†`¶€?K.”^n¥K?(NÞ÷—õ¾,‚x`‹ÔpBCÿ÷t¹?èÈ«O)‹0I ÜaÔ¨ú¼àm Žâ=€BôÆÌZ,PÝ{ï½÷^S)þò—¿üEÖ{›¦i¾÷Þ{ïÉz ùÃþð‡¸cPî¿¿…ç@r°$W݃+<1ò[Z4#Ðoæ€ò{Œ¿MVƒ–Pù͈kÀ;Q €Š°¿¸½ÿþûïûÿN§sxxxè÷·ív»½···7ìß2™LfjjjJDŒHŒê1Fh¤+êF”8e)yX I¨————qÇalån'­iXªl‚‚xåB¼ñ+‹Å–×6F*•JíîîîÊŽKI¬Ào||||lll,î8 Ã06666ÒétÚï5?þøã†a°ië$j­å(çg"^¹ñêå«¿uR,¢Þñ"^㥠ŒÊÄ=êOýdËw}ûù²¹ŸoºÅ â%a–„“ĪïÀRæÎÏ ¾9–Ëår˜÷Éår¹7oÞ¼‘Ý‘B¼ˆ×I·x©òù|þÙ³gÏ‚Fúý~iii)ÎQÿ|>Ÿ¿yóæÍ¸>ß0’_€Ýÿ9⫯¾úÊ/½½½½Íf³ÃþMÔ–Ñçg!¡Ø™Í° e7»á,:CŽxßB¼oé/ÖΫÕj½ûî»ï6›ÍfT±¹™¦i>xðàAP¬²ŒB}šÃÃÃÃN§Óöoét:½¿¿¿ïžb?O …BÁë}ºÝn7LlÅb±è÷Ã> à–Ïçó?üðÃ^™MvvvvD­©E¼¿‡xõ‰—‡eYÖ“'Ožu¨Fcnnn.lC$¬»wïÞÍår¹8>{êеÛíöÑÑÑ‘ßkÊårÙ9åþäääÄïyÒét:>|&.Ó4Í –×:g1 \XGE(—Ëå°fÄë ñÊ%"^–eY‡‡‡‡AÚZ­V[]]]*./qNý…úünß¾}Ûk½½½½v»Ýó++++,Is÷q…H먢Hår¹Ì;}ñC¼r…‰—Kçß^ï_©T*QÅå%Ωÿ£P œv»ÝÞÜÜÜñ^­V«uïÞ½{aÞƒ2útttäL6 $¦išOŸ>}*s𬗠V†A¼ì¯\<ñò`ùŽN§óÑG}çz'ÖQ ÑF¡>€»»»»µZ­æ=:NçÚµk×Â.¹£<7þùçŸÿĹF7N§PPŸ£Nõz½ŽxÁ‰zmá7Iê}u€¦Z­V)÷½¡ÏÝ:PˆWn¼ºAùF‡zà ‚Ë6žFóñññ±ßÚYÄ‹xuŽwPî£A¨ Ô‡_ñ”Å`ß³M·xG €€<ÔëËëÙˆ%È4Msqqq‘ò7”#NšÍfóÑ£G(ï?3333;;;;ìßïo!^½âù|>õêÕ«q|6êïòù|þæÍ›7yþvsss3ê$u‹@$êҵׯ_¿~õêÕ+÷ÿGÍÎÎÎÎÌÌÌPþfkkk‹²ÑÉÇR6SI§Óéååååaÿ†xñêoÒ™¦i>xðàA:Nû½îÿþïÿþOÆç£>¼Åú; S«ÕjQï`®[¼"ñ$¯ÏÎÎΆ=»€@ËËËË”FW«Õj=þü9å3Úív{oooò7‹‹‹‹Ã¦Í"ÞßC¼úÄË«X,u˜nÉrô^£Ñhü÷ÿ÷ËøüQ©Ax@¤Ì†Iõxƒ¦=ÇqÝñ,— .§‘-è¾Æº4@$ݯ-$ Ðûï¿ÿ>åõ<Çœ¼xñâE¿ßï³¾ÞkÚ,âñ²‰;^ ç:ú–¿) ûo¢^Í2…»Óétnß¾}[V I®¬x§Ò÷ûýþ;wîD}Œ¤ŒxE­U·¯Áýýý}¿Ä’󺓱éã°Žr¹\.Sß'N§÷÷÷÷ãî´Øåt_+—ËeqònàÉBÄ}6нtÚß*Ìf Ô=W’vmÀ²,k~~~žò7?þøã<ŸõêÕ«W¯_¿~Íúút:žœœœtþ?Äë ñ²‰3^VvC-ìzÙl6{rrrEcŽu ·ÌµÚI­a¦Ò?zôèQ³ÙlʈˋªñÚFžkpgggGÄ5ç…dMR9;-QŒ¶ó–«'6X AÇÞz©Õjµ¹¹¹¹ $f¯-'$À×ÔÔÔT&“ɰ¾¾Óéty>«ÛívÏÎÎÎ(óá‡~èüoÄë ñ‹;Þ öHDØŽ¿›9M™e 7Ït{ФÕªO¥wS1Þ|>ŸöìÙ3ž¤„-›Íføá‡xFíÑOžQÈ0ÊårYf²PD¹ær¹Ü/¿üò‹*S­A¼z½^çy–J¥R¥R©ø½&©×–àkrrr’ò0¾¸¸¸¸¼¼¼äý¼ŸþùgÊëÝëf¯?Äë/îxýT«Õª¬‘[¡P(Èh„X–e­­­­ù½&ŠéåIª<0õ_LLa;©¶t:~öìÙ3:«a~F½\MµZ­òvþUßTÖµ5 à‹:"åµã(+êtÛññññ±±±1û¿¯?Äë/îx½ÔëõzT#Ùl6{xxx(2 °½½½ttQÓË“Rx„™JëÖ­[:Mý—o˜˜¼¤Óéô“'OžÄ¹žšJtÌ–eYOžŸÏõÕW_E“S.—Ëݽ{÷.ïß³ŒìE5¬{}àfں̼¨o.—ËÉ¥vÚØØØ=  ßï÷—–––RjµZúÞét:½±±±&¾ñññq‘Sÿ‡‘Q® Þç`Õ¯-?H€0ý~¿OÝõÚíòòòòâââ‚ò7¼;g#^6ˆ—Èx7666d7ŒYܼyó&ÏHËì…¸F˜Y¨Vx©8•ÞnñŠ$j@­V«ÙL&“aÙ¡R©Tx:+óóó󪯳×qvðo )³óŸ”k €é¶ÇõȬ¸!^¹otÂLýoµZ­+W®\qB”J¥ÏûñŒD˜¦i²üM”#Ì:×^*N¥÷£[¼†ñ¶³!bÄÏÆ;ZÝét:ÓÓÓÓ©T*tÔ™ŸJ¥R¡ÄŸÍf³ ¼Ÿç%êrm6›ÍL&“ ͵5o,ŒwcHU®­n·Û›››c­£©T*µººº:콑¥ðœ'Ä+×(ÆËÚyv³)ssssÃÖÓïîîîò6\©ëYFq“0b$ÎúË;•¾ßï÷¯_¿~]—©ÿqÅÛh4©T*5¬W*•Ê•+W®´Z­õ}gffffgggY_o'ü&&&&D•Á½{÷îQb9kE•r…øX–eR—܈îü'íÚrB„éõz½ÓÓÓÓ¨?—º³· ñ²A¼r¹ã™™™¡¼G«Õj}ðÁ°4RVWWW©#iét:½¼¼¼ÌòZ–QÜ~¿ßßÚÚÚ¢Ä5Uê/Þ©ôQÇ8ŒNñ6†×Èš­Ûív?þøã©UÖ} ‚~at»ÝîÁÁÁëëE¬±—]®¬÷0ˆišæÓ§OŸÆÙùOâµå†xšœœœTa 2+Ä+âÆòòò2%îN§Ó¹víÚ5JC¥R©T¨3ÖÖÖÖ‚Ö#²ŽâÆÑiÓµ>ðàJÕqŒn:ÅKYnÐív»wîܹC= òÓO?ý4è}WVVVdÎz œ€1>>>>666æó¢(WQÉ5Ã4Mó»ï¾ûŽšÙùOâµ5 Š0MÓ\\\\¤üÍÞÞÞOcekkk‹Ò€f9¢Že7®Næ¨3•þÎ;wdÇè¦[¼Ô}+šÍfóåË—/)Ÿ1999©Ó®õ"ޝD¹ÂãÇÇÙùW‘¬£a‘Puú§Óé<|øð!ÏgQÐAShY§þÇÑi%:M¥7 ½âít:ÃÃÃCêßQ“m£¶^å õz½NÝø6é™þ)î@-Ak¯@ê4õ££££0S·¶¶¶®^½z•õ3íõˆÃ:ð,Ǿ|ùòeÌQ¡ÓTzÃÐ/^ÞëíÕ«W¯^¿~ýš5ÑÁº@b±XÜÙÙÙ û>AÂÆ«[¹‚X<'ÞÄÝù×åÚò‚Š îøûÍ7ß|æóNOOO{½^õõ^ëYŽ-Œó¨¶Q ÛTzÝâ5 þëºñ/÷QÃQtPDP½\Aý~¿¿´´´uç_×kË Š lRÅ;mÖ©Ýn·ŽŽŽX_?l=¢eYÖýû÷ïý-u/ÐðN¥¿uëÖ-¦þÇo¿ßï‡9Ò‘²ñ—a°'óù|¾×ëõƒÁ€g$5nª–+¨)ÊYº_[~OggggÔ]vã„xåB¼r™¦iR6————a?÷çŸþ9ÌßooooÙÔh4q¯ÕÔ­>PðN¥ëwÑ-^Ã,$µþ%MÓ4÷÷÷÷u>ÝBµrõÝ¿ÿ~Љ4a$åÚòƒ@„VWWWS1ºråÊêÙ¹²v+ ÂÛA¼l¯\v¼ccccããããQ>eÍ=úÂ2õ¿ßï÷·¶¶¶Âć¸ë+Þ©ôq-ÉÐ-^[Ø„u¹˲¬Ÿ~úé'žªQ©\AÙl6»½½½-㽓tmùA”Bâ• ñz;;;;‹s7}Ö©ÿqí.¯‚(êïTú¸–dè¯-îëÍfY–uxxx4ëFª”+è¥P(ŠÅbQä{&íÚòƒxÒ-³ŽxåB¼`´5´qíÖ>LëƒnSéu‹W5¦išOŸ>}: € "—ŒÚµ…#bsžiÐÔM€lˆ— âee¼:Èår¹7oÞ¼p(—ËeÖÏI§ÓéýýýýaïS¯×ë<±«^t›J¯[¼q›œœœ4MÓtþ?ÞÙð«aå zÊf³Ùõõõuï5j×Ö;Ýn·f÷ͨ!^pBùÊuyyyyqqqw~œ;#^ñ¯\awÀvI«< Ö~¿ß¿~ýúu]¦þÇoÜÜSã-˲ÖÖÖÖ¼§}„ËžA¥R©þ[¨K’åæÍ›7óù|>Ì{Œâµ…ðîs.UÅ;¢4< –°GìLMMMe2™ ëë;#Þ`ˆ×_œñêB× Iª¼SéãÚA·xU´°°°À;=¹T*•R©T*“ÉdPž ²N§Óùøã?ît:–×§Óéôƒ„™Õ1Š×à‹º#uØ#v&'''û‚xý!^qÇKý,Le¥IB}°,ËzòäÉêûƵƒnñz‰ûzûôÓO?¥þM§ÓéLOOO«¼BÜå êèt:…………ýýýý½½½=Ö¿Ëår¹»wïÞåýܤ^[~_ÔѾ°sꈛû!Äëñú‹3Þ¸¦¨SbÖ}É‚NõÁËööö6u´ªßï÷ïܹs'Ž©ÏºÅëe||||lllŒ÷侀AœxO“srBØÙ/¬â,WP‡Ýù·ëë½{÷îQŽ­æ] äkËÏ;†ÁfrTÜDÄ+VØ3Xã†ò•ëììì¬ßï÷Y_öaNq;888p6¯?Äë/Îx©SÔÃÆj ;Ê­êÃ0Åb±X( Ô8âšJ¯[¼~2™LfjjjŠ÷侀AœmžÍ$[­VëùóçÏ)‡8ËÔàîüÆÛçáÖÖÖë{ð.Hòµå‡< ì…jÑž“ŒxÁ åKG=:+›Ífx>‹§lÝ#zˆ×â w¼”Æi˜Xm–eYóóóó¬¯×qÏ'ÝꃓeYÖýû÷ïSãˆsê¿Nñ {*uÔÏYxF¹ƒ’IA¢J ÆY® †££££a£é»»»»F£Áú>¹\.·²²²Bùì$_[~Þ1 ÚÅDZ8î;¯¿°ñêå+W»ÝnQþ†wzÓììììÌÌÌ ëë‡MGF¼Þ/›8ã¥6NyÖ.:Q?ºÏhÒ­>8ñL¥ït:k×®]Óeêœñ²à½ÞLÓ4EÇã'LG7êÊUÔåKªnmmmmQfmÝ¿ÿ¾eY–̘tº¶¼¼cô)qa×.Pw[tˆ ^aãÕ ÊW>êwX\\\äY7»¼¼¼L™Ê÷úõëׯ^½zåþÿˆw8ÄË&Îx©÷³ùùùù0 JÌaG>T S}°ñN¥³N5 ÝâeÅ{½…ME¹¹¨aÐã +®rM2êò%ž©ðQh6›ÍG=b}}6›Ínooo³¾>é×–—w ƒ>%nmmm-Lƒƒšé ;¥ñúÓ}:Ê—]·ÛíÎÍÍÍQººººêü»/^¼ tJx¦añœÃêÕA¼¿‡xõˆ÷Õ«W¯^¿~ýšõ=²Ùlv}}}ò¹¶|>Ÿ¿zõêUÖ×÷ûýþ‹/^ð|–Jtªö{ñL¥o48v¨Ö-^ Þ%!ÔD›;DM †EM^…W¹&µLUÞLñáÇY4 Ã( …b±XdymÒ¯-/ï}çá¨îlâõ&"^Ý |å£vJ ã탙2j¶¾¾¾N™Yá×A¼¿‡xõˆ·ÛívXßÇ0øw?¦6žÝëÿÛív{bbb"(¡È«V«ÕXcë÷ûý¥¥¥%–„¦NõÁ0ø§Òß¾}û6åoDÑ-^*êcj»Á0â]:×´ú¤—k(VÔŽ©ˆ=µXµÛíöæææ&åo¢X @¥ä’•jµZ±fWl–eYçççç”Ï8>>>öÐE¼râ­×ëujœq¨×ëuËWg>>¦¾¿»®ñÄÊ£[ØßPårYÏ(íåóóóó°Qžre½/òþf^åI½²üî<¿9Ë=[§kK Þ d>ýÞñÊW7(_ùxAeÀ[¶A7DÄ‹xu—·qËÚÈäIôR;Ø"ÈLèPxcämœ†5*ñ²ÆÍ{«Ë<ïÅÓé S&"ƒA´å† –Ïæ-ÏÁÀû~(#Àó¾,¿¹N×–a*€_p¼ÃÁÀÿ‡C¼rãÕ Ê7<£fƒ÷Í’wÆ ëh'âE¼ºÆË›Ôô{ÿ0ïG'Mf€úþNQÕ‡0¿W”캡[¼a;ª¶aíÞº5xמúE„ÕŽÒ­\yé’° « a¯Yç}ײ,ë¯ýë_ C^€ZîƒÛï®Ëµ%M˜ K Ù!H IDATS:eJZ¼ºAùÊ'êÁëÍñ"^]ã Û`)®d¦ì€êõA·µnñªòû»‰®²¦A{Å­S¹êPgU¯¯Nv¬2<åt èrm‰ôŽó?¨»,ʶµµµå·Yâ '(^Ý |åk·Ûí½½½½8chµZ­çÏŸ?gy-â¥C¼r±ÆÛív»wîܹåîÄ^=zô¨Ùl6ãŽC4ꌿúpxxxÈÓÆ)—Ëe¯™Ý99999¡nܨ\gÉÁsßÚ(×–¡Î(ªì)¢%5^Ý |å‹sd’g”ñ"^ã{„*Î{™ì†¡v}ˆû·g5*3:N‡wI « ‘>UÚ8ÃðÎ8>>>–=²­ó,ÌsßÖm–¶S$›¤ª0í²VñÊW7(ßhÄõ â½ "^Ä«s¼qÎ÷½,Š€a¨[tèœ £“¨ÿƒì¸T¬«,xõz½.³óå×ñÓ¡Î" îþèwÏU¹|#I¨PÔ/Šx寫”o4ÂlÈ#ì‰ ˆñêoÔI€¸;ÿ†]À0Ô¬:tNƒÑJÈj_P®·¨Êùþçþ‡òú0 Yƒ7AåªC#ðÅ_|Áó{D™0 úsQ•úÀ{mEBÕŒ8â'^Ý |£U£YÔqŠˆñêoTSUèüS¿¯ˆ U«:tNƒÑJȨ'*. *þõot+WêlÔ €0×sÔ ž:ôY*_[‘‰r:µˆ‡7╯nP¾ÑýP}ãC¼ˆWçxe7Nâ:“}˜¨†¡V}Сs2Œ^À0ÄuVÃÔ[Yåm×Ѩq”«u–·¾òì­àLþò´¡£N_b<®åWa¯­ÈÉΊ‹A¼ÑÄ«”o4DNÊÞ| ñ"^ã½$@Å$f žÏfÁStèœ £™0Œðm ÷‘ +÷ÌŸ8"ÊUÅåa„©¯”ï7ìÊÛ‘2Às °Ô¯­ØˆÎŒËþBˆw´¦£AùF#lÃ9ê]Ç/âÕ9Þ°‰U¦ûg€'†aÂÔ:'ƒÁè&lÔkPF] ÓiöºÄ•à-Wg ¬t¨³aë+Ëwô{°Ö-ç½.ÊïïÈú™*][Ê ~¸§"^pBùÊÇZƪ”-â• ñÊ¥[¼ºAù&‹¨Žª-¨~D5[å{©8ãÇKPlT®·°õuXB…R¼’¡£4 6i×ÀÈ õDz'î@>$F# €€À@` 0HŒ$F# €„˜œœœ4MÓŒ;P# €€€âLÓ4.çççç–eY<ïiY–u~~~î~Ïãããc[aÕëõºûãV¯×ëAïãõ¼bù[ˆžßï¦Q­V«^ïÛëõzù|>/ú»ÄÆëZ,‹<ïçõ Eƒ Âbéü³vÞòù|¾×ëõÜW­V«Q~'`ç÷ûó ^ø½:ÿHÅb±(êÁçÕ˜ ø †á?RëÇ+íÕùC@]^3©m— ÷A»Ëk $åAjþÿ0K üžU¬œ:¯ä7:~êc© ~¿!Ëß# ‰4¥Ò/à×ñ·aú?„Á;úO…ißzðKàØ†uâÑf0؈¼Ð˜€°(kÿÃÀ†µú`I 9;ó,Iüþ02d®`„á7eÛÙa½LÔÇ’:>>>þë_ÿú× ×a¹"ŒÑItþ ,¯Yj^6–‘Þa0ú«–ÍüX`¶"Œ,QItþ@¯€ßZmjÇ@}aæ@Ha2êA‘¼,‰f–Ùèüë/Ì^FØôÀ%³Žust”MÍpÁ(óêÄSFmÃ$@<31h‘a­ :UJjæ SmâCÝEf@¬$>t¢Êó*®žÖçŸþ9îMŒ¨ 2“”µ7ÅP%SªC=ÐAŸ:Qéy÷ <€‡} a "'b*WC@V¶›w]­ê7YÖF :ÿêamb}@xI}èD¥çUÜ ÃÀ3èì:ƒJ¿òz¾‡¹N¼ú©¡¯=ÖL¸W§šu$Cdã…µÅÒ‰:À»»¶ÊgÖï¤òwuø ¢‘Äg€NT»×©0 õÊÔån7#9 ð«a÷ô0ÉS¯½/B÷SuLP˜A…Îs¬¯0»SÊŠ),ÖsVÑhPkPÅzÉB=ÎG—:™Äg€NT|^©’0 <€»ž`À¯¼îé<׉_[hä¼£'^åõ~¢×º…9RËI¥L+k݉«óOilcmã[¬QË‹z ¢QãMöµ•Ä@Ÿ:Qõy¥R€ŽÏÏë:Â} TC¹·Š¬¿~‰fJ»2è™:fÔFaP~?’è›Kââ‹/¾ø"¨¨ÊH:kc6ÊxE5°U)㸰Ü4uY Ê:âÇBµ„˽WTÌQ_[IL$í@e}uSñyeS-À“.Ï•‰|>ùy”6™á3d~GÊ3G·äeàTçëšw€Ø-ì}–¥.ù=ë";vY§@T7KÑY–2²Ë‡å÷P¡±Ër¡EÕX ;­Ön7zQX€*wDdÞ+TéúD<È㺶’–Hâ3€*ŠúêE¥ç•›Š Ö¸T~¨HT"•JÔýÂ/~‘×/Ïó;ŠN›M·va’²¯)Þöå¾ÎCÈ5­S@vÚDŽB°”¯»l)Å8°Üœ£¸‰ˆ˜ÂJ•N_”X®7Õ:"Q4°T¨ ²GSã¾¶’”Hâ3€*ÎÑUžW^TM°Æ¦òµ§Š¸:þ¢£ k)ªÏñ¦cŽÀ[:%DøáyÖÊ<öé’ð»:o:¢— „ôõºa•k\™w–òâÕl(¿“ŠX€ª< £jd©ÐñbyðñƩµ•¤@ÒžöýË}‰¸®ÂÌBã½" î÷“%ŽÎÿ`þù$ª}*¼Þéž`)–EWz¿‡%õAéWy¢ºXƒnÈQÄçT:•oŠQáÊ,[Ô…8²FSU»¶’HÚ3€G\£ÿ*<¯Xé’`‰UÅë02’©a6M û» {ÆúmZ¶£&À{ ð–J÷F§¸:ÿƒ˜6_˜¤ j'PEÎëǧü0^¸è‹ÙïæÅ{qù=PdWŽ c×üÇñuÔ°‰ãÁõÈuœ £©*^[º'’ö àÇè¿ Ï«$Sñ ’8ÖýÊN¸s÷ýÆýÃ&ªÂ>“xî)H¼¥âý1ê5ÿn¢ŸQ,ÉŒ¸“½ÀÁ¯¢†¹°‚²G²nHAŸ›´Ýþãüž:P¡>8é¶, –ë€úÐPõÚÒ9´g/õ5ˆj÷§$B{“ù<Š+0ì;±œZæ~6Àó}‘xKÅë7®å‰¶¸—}òz'îFÍ?ýÓ?ýÓ|ðAjˆL&“i6›Mž÷ív»Ý¹¹¹¹aï›J¥Rß~ûí·¢¿‹íßþíßþÍësÃ|'VÛÛÛÛÙl6æ=jµZÍë;¤R©T£ÑhˆŠ7É‚êá»ï¾ûî•+W®DÏòòòr:Nóüm«Õj]¹råŠ×w©Õj5Ññ†±°°°tìíííµÛí6ë{âÚ/‰Ï2ê+‹¸ŸWI§Ú3@%¼Ï#U¹¿S§Óé<|øð¡ó5Íf³ùòåË—Îÿ·¶¶¶×æÕ«W¯ªÖ‰>Åb±X( aÞï­7===Ýét:¢âAÂd‚y2™^Y`³¢£ŽwMϨ£3ƒG6˜å»R¿—Ê×–Î3@N}P™ìÙTqÌ Œì‹œ bYõ³1€Ì¶qØÍ4©¿“W9a@ÄLÓ47666xþ¶Ñh4xF{Úív{bbb¢T*•x>¢36666>>>Nù›N§ÓYXXX Ž:V*•J*߬€••••\.—ó{ e4×È$º¾¨ŽeÆ‹nXFÿmªÍX\\\Tq_”¤I§Óéýýý}g‡YÔž4,Ï‘aúý~iii©R©T(W©T*W®\¹ÒjµZÔÏx×Ó‰ tf„1@=<£:freŒ¦ª~ma€¾0ú£Hö:å¨gðìî/j€ˆÔ6fÈÁ[V¼£ÿ¢ÚêqÏú3@[<ëéZ­VëÆ7D|~»Ýn/,,,`}š>üðÃ)¯oµZ­çÏŸ?—,,Y𣣣#Êh*®-EF}P™išæääädÜqˆ´¾¾¾îœÑà7úo6 ®‘øt:æåâ”Ëå2O'zvvvvfff†úy·nݺ%bŸ—J¥RYZZZê÷ûý°ïîOÆ(®QRÄš¼X-˲æççç)Óï÷ûwîܹÓív»a?ß6êêkƒƒƒ‘u# ,Sõûý~kkk‹õ=GñÚ šñ€bȨ¯¬‚FŽeÜò,GS™eYÖÚÚÚšóÿ±&í¶¶¶¶œ¦\.—[YYY‘gùùùù8fY–eGñÙõz½®ÃõÎÎÎå9Ë3HÑh4»»»»ôè†k6›ÍO>ùäI¿¶t¬mfYç@ËøRˆu4bå‰Gæ´¬ù¯—yüOTÓeU«A¨S.uxHº±Ô+jçU‡kKÔži¹aêIîaȨ¯¬ï«J¹"V9±zá™*ìõÞhß+¢8Ž6Ê%îÎ å>ì÷ ® Ùa½Ÿ‹Zà|Ù×€óùæ÷=ã\àÆRy6ÔTñùÈs_V>¼í!ÏÎ ±³³³#ãË;7‡Ó€D¬¿5 ±~úé§ŸR>§ßï÷_¼xñ‚!›ÿïd½\T®"õûýþÙÙÙYŸFPœg4u®-ûÁËs|ÐÎÎÎŽŠ Ȩ¯^êõzýÍ›7o¨…e³ÙìÉÉɉnk¹£0lôÿåË—/Y§Uw»Ý®ûsõy†eYÖáá᡽t"›ÍfeuC¡P(H›öX…0¨*Ä:š±òdÿâžÂ«ÛHŠêu@tü:vèdŒ¦êrm…™ êZäyëvIÖè?ë{«R®ˆUN¬N"Ÿ_Î:‰áFÿý~Ê,Öß¡×ëõ¾þú믃^ÃØ~/úZðšÙæõ]Uš`9cA¥e§"ïv Ÿ‡l6›ýá‡~På‡òƒXå`‰•'û÷Í7ß|>:ˆBõõââââòòò2ªÏAÆhjÒ¯­|>ŸöìÙ3jÖ}˜t:~öìÙ³ÿŸ½ó m#Mó9ÌQ‡òÁr.‰Œ§OéD»°¿u/&rÆÒ†ô6x‰”Ç%Éàœ¦÷”0–À9Ä`ØHï.–€drˆe²ƒ/³¬-§ûÔk¢äbËëUÝý;dkZ]]Þç­÷­z«ôýÜ┪•ÞÏÿ(¬«*ï?]×õ7oÞ¼áif‹GrXÐu]¿uëÖ­Á¿Q¼ÿ&AFüùÏþ³[™D"‘˜ŸŸŸýÜA¬ž+"#¬žÿA¢´g-///;„¾øâ‹/(÷:>>>>:::#?"Ïšæ?ªC)€¦} [[[[*{2L «¼d½|ùòeÊŠjx÷0¥ñÙl6;;;;ëv Ϧ繥뺾¶¶¶&jóÕ4ŒSVdW¬¼xñâ…(åßäÁƒü(M>|øÐï÷û"e k?F»k?uáøN§spppàvÌN^Ê¿‰#€›òo#€“Aˆ§HñÉÉÉIØžÓétzkkkKäùCÓ4íéÓ§OyÇŒrMû<ž={ö,l9X€¬rp“•ÚÞ Ëh¥ñ4óóóó^Éúúú:uÓ‹óÜzüøñcÑŠ¦}§/_¾|F+«¨ k¼0H¡P(ðÔõð"‘H$ÖÖÖÖÆÆÆÆx>ÿéÓ§Ogggg¢å »~Ö»(€ÙÙÙYŠ©µó€YѬʿ‰Ÿ½„Eù7ñc0 Ø›››ñ`lllÌjà¡bgŠb”¢®ëúË—/_²Ž ©T*õ‡?üá<ŸUÒ iò@V98ÉJ ÿQÁúøˆÒx »"LVÚív{ggg‡zï¸Î­Ë—/_özg~³ˆ•êȯ˜¤ÓéôÓ§OŸÊº&“ÉÌÍÍÍñ|¶×ëõ¢)å„Õû¯iþvûûûûƒáù‰D"áÕ&”‡££££ãããc·kdDüêW¿úu]Ëd2™7oÞ¼¡(Ê¿I·ÛíþïÿþïÿR>C¡×ëõ¦§§§GFFFŠÅb‘çvm£¥(Ëù`’Éd2<Æ_VKPµZ­Rï-z!€¬ñU×uýòåË—)÷ùñǤ>°¥ñ––––¼{žƒYœçïIÁ-gq˜‘5^„eœùÅO®xTÖJ;ì¼ÿ"ŒvN§³½½½=ø7Fÿ^¯×ÛÛÛÛs»ÆNÙôËÿüÏÿüÏï~÷»ßQÏ>#òßn·Ûó7ó7Au®ª×ëõ¹¹¹9·( ;R©TjffffðoÔ(Å~¿ßÿðáÃÊgDÂb ² Z­VÍ}2™L²ÿ(—ËeÀï„„¬ñ“utttt|||œrï¿ÿþ{ÊõÀ›(×8!ӛйåDüxÿA¨|È6ÙØØØp+F§22¼ÿ&Ö÷"Ëèÿúõë×n ¨²)Šr¹\–aàUþoß¾};hƒk«ÕjmnnnR?GUø­„]à9Ã$/L€n·Ûœœœ)—ËeÞ‡Q'Ï„„¬ÞÄ]ViµZ­d2™ôò’›4›ÍfØ2[Á–ÍdoooÞT6ŠÅbQD¤Š‰W@Ö A×(½WÈ*ž™™™ÞCv»Ýn ~ÉÉÉIÑÊ:K1:U±†Ç‹4ÚÐjµZoß¾}ëvÌ(.ÑF€()ÿ&ŸÏ‹2PÚLOŸÝÂè–\œA¼Ús@ÖÏ ³¬@jÁ²™looo‹2ÒÄ•f³ÙÌçóy·kz½^ïöíÛ·©F€D"‘ ÖQˆ+¯ xÚ‚†aܹsçŽWêZ½^¯‹4ð†A‡Éòòòò`Ñ5);vQ‹‹‹‹¢Sÿ¬E­È2< â×UåßÄO-Œ¨Õ)â1LV«ÕªW„m§ÓéÌÌÌÌø5üÂÐëõz 27eJÞèøøøøèèè¨ÝÿAÖŸ3 ²R+€†]$ª¨<4íóFpxxxxáÀùùù9¥êj&“ÉœŸŸŸ;ÝωB¡P ~oìŠ0YñëM†¹Õív»=zÄrm¯×ë­¬¬¬P=v2ªIG Æ+šÆçeÛÜÜÜd©[£iŸ~Ò‚¬P£ßÂÄÎû/+ÅÌš£ŸJ¥RKKKK"Ÿagh°"Ãð`…×p~~~eå_ÓèuƒÜt•á1L¶Ûíö“'Ož°\Ûét:÷îÝ»ç'¢HÙ6€&Éd2yõêÕ«aËÁd•C²f³Ùl¿ßïSÀ0•Âa"JãUvE˜¬¨êMUin­®®®RÞKÞ¨•(‡,Š"ÊãD jzáId?Þè¢0°zÿeíìÖZÊx˜Åá1PQMù&xR*WVVV(¿Ïùdå 0ÌÀ›*†n·ÛÝßßß§~Žš·;ìu0^AP‹‚ñžX<ÇÌé*ù´ó`Ê6ÚY×ZQGGGGÇÇÇÇn×Å%Óå?\¨•ÇÇÇÇGGGGÔçø©+Âm( ^/N­V«±>GDN%d…¬@òañ¦À›êï;b902ÌcUÓ0^ApPs‚ Ã0^¿~ýšçY^žcòù|^t¡AQX;xa´ "  ×ëõÖ×××Ý® ²õ° #€ÊÊ?5b'ì6~¼P¿'oj õ|2³ Ñh4xïAYå%Y0‚ÇËa†áu Þ  ¬v0^APŒŽŽŽŽ³^ï§nɇ>ôûý>Ïgݨ×ëuÞ®#²°ëàTÊŽ]€è|¯b€2"ÜiPYù×4zÄNT¡|O?†I?çWÀ`î&µðDÐ@V9DIV Œð°+ÂdåíÛ·oY Z +~[R  kÛJŒW$Ô<[?‘'<-ÌX1SFFFFdç…³†÷ßÄ. `yyyYd]–ß2èÎC"Œª+ÿ<…ñNNNNTý>NP#“üT¦žOLl f…íÝÝÝ]JC@V9¨*ëÉÉÉ % oØ‹ÇùAÕ10LX‹0YéMóÜò»ÁRßͰx9¬9^ â·%X-ÅÊåryäÿ˜›››và…÷?è”k´V&“É,,,,È|†•0j¹ø1¨®ük_ÇŽ0ÛøñBLò›æ@=Ÿ˜üÂN§Ó?üðÔZaYå%Y0ÂÞTqøÝ`e…ÿÆ ŒW4ÔB[Q£Õjµ’Éd2Hc€Õû†ÑngggÇš!: Àîƒ$‰„W1SÀN6›Í>þü9õsƒÞí^¯×£FòEÁï7Ê÷|ò3@:NïïïïS-4aYå º¬Ô>ì¹xP} A{Sã<·¢F5àýªÃ*+êóª£ëº~ëÖ­[ƒ ÃhgW¨OtKîtÅ5MÓ*•J¥T*•x>›Éd2oÞ¼y£b ÚB¡Pà‰$åíÜØø‹@×uýåË—/£p臬rˆ’¬@Qƒ9“vP‹*µÛíöØØØ˜Óýœ¨×ëußÅ›ÊÛ6Ñ`¼}nܸqcjjjjðo¼ÅSýbW¨Ot{>¯Î2 :áGù7QÉ`¦ú)m—zBM ¸|ùòeÞ‡ŠüÅðøñãÇQ ÷…¬rˆ‚¬Ÿ>}útvvvFṵ̀äâ! c`˜ŸŸŸ÷²–¯¯¯¯‹ôjcn‰cØaŒW€XìZЉ½g%ˆT»‚ƒVDì¡ü›È6$‰Äîîî®W›çóóós¿gIƧññññÑÑÑQ¿÷‰#—4;èÃ0Œ¹¹¹9¯™Ÿ¾§u¸e••4˜kgÇäää¤[Û˜8•1wX~‡v»ÝÞÙÙÙù\Ì-q SÊAXã – BïY°[Sd¥"x)™³³³³Ùl6+ú¹&"••"xqÚ3¨i8Q*V4—4¯2£I±X,ŽŒŒŒ$“ÉdyBUQ’!@rˆÒˆ3Vχ²¼©˜[€J˜ã »âxAxÁ±žEdÖ±K5$‘H$æçççe<[†òou#€ÓžA­x…ZE~ÏQÔv¨&—4orw»Ýîää䤬X' «¢$+ÕF;—(¥1WÂö¦ÆunÁP!‡°Ç+ü¦, CÊ“]€l/ø º®ëÖêû2 v:ÎÁÁÁÛ5·nݺ%zÿ©ü›d2™Ì‹/^È|† ªÕjÕéLÉSñ>h5Òošo7”Kº®ë<Ö‘ÕÕÕUÞ¾ ¼‹(dugdÕ4>  , n\ˆÚˆ+,ÞÔ½½½=YÞÔ¸Î-¿,¯…=î„=^ÁpÃÛÿ¸c²%Þ‚5w\v!Âõõõu·q$: ‚Gù7 7›Í&ås¹\.×h44 ãÙl6ËårÙéÿY 6VTwøMSà=K_§|(,‹>d•C”dÕ4ûB5^D¡h˜Dm Äoj·ÛínlllÈ’!®sËïKµ°SS)¢ˆ ã ~ת(¬u"°k‘D€÷?ˆs˾÷›ßüæ7"žÅ«üß¾}ûv¯×ëåóù|\ÕjµšÏçó^×Q÷× #©õ”ü8RìZw²r‰Ç³áעϻˆBVw†AVMã+Vd[‰Úˆ#,5¶···y#.XˆëÜò›Hµ†{aê IDAT°Ç½_¸¦©1^ÁpC öÓ×=N§oÞ¼y“ç³Qdcccc07>ˆ(;ïõCì V2™LÆo{d¿Ê¿ù·¸ÌâÑnžÿA¼Ú7Z 2‚Å„j¤àM3±kÝÉÊ%ïK~‰ŸÃ o¨1/UaËJ cˆ;a8açù°”75®s‹7Ð…=®¨4^ÁðB͵õ 4li@N§³½½½=ø7?¬ktQ†Vƒ‡hD)ÿ&q1‹Å"µx4OÙc×JPõ”–———yòÿ5MÓ.ñàŵ²:3,²šxUnµ# žÊ°ˆâˆvž+AySã:·xÔ±j†A¢ˆ*W0¼j;???ä©V¥8•J¥––––d<+›Ífggggÿd÷žÜrVD+ÿ&Q54›Í¦Ù˜§€4Ïo•J¥RÏž={F}–Ùl6ûÛßþö·Ö¿óÔSZ[[[£DØÍ —‚.¢âg…¬Î ‹¬&< Ï¢8â‚jÞԸέT*•š™™™¡~Žja?>>>>:::¢>'*¨6^ÁpC µ]\\\¤Yê]Ä»(ž÷Ç‚uív»Ýýýý}ÑÏqCF±AYÊ¿‰êF3¼d–</¼ 7Ú‘Ëår…B¡à÷Ù&Ùl6ûêÕ«WvçžzJ™L&óøñãÇ,צÓéôÖÖÖ–Ÿs4W /Q ¥„¬r)+Ï@™`@Q¯AÀâM=888Ò›×¹õôéÓ§”Ã+…ýäää$ÎUïU¯`x¡†Úòx±Yº]Äk¾µŒ(»u6Œ"k÷¿d³ÙìƒP>CQþMx<ʰBï5¼Ÿ[Óèg'*•Jewww×Ig©+aG©T*U*•ŠÛ5ét:½¿¿¿ïwMºdOE–ƒƒÕža’uV«Õzûöí[êçX&ذÕ1¼Þ£a†µ/³lâ:·(a€º®ëkkkkT {Ü;¨8^Áð“²ôàÁƒ¬)K…B¡ »_»ÊØí¢£ì¼ÿaDñ*mN´Z­Öõëׯ³ŽÏn·Û½{÷î]2ÕP,‹<á÷ªÐëõz<ûL*•J½{÷îoÊ¢®ëúááá!Ëš@-VhR*•Jý~¿o'c£Ñh¼ÿþ½ƒä%jMãË¥L§Óé§OŸ>¥‰÷s ë/6Y­ðx*5íó;<<Q7U‰Ò( ë¢h…²HkÚOcÁë¾,8YníðÊ/Ó›繕Ëårv!ÔM~¸çÿ«<^Áð“»m°íŒ•ét:}zzz åÿ3v{Áòòò²#¯5½"ìú!¼¡ånxD(ÿ&^F€¸(ÿšÆ`R*•Jn s-¸¸¸¸à‰âu¤ËÚëÉàËãeðY!«Œû‹Ä”•÷É '4*c@õ÷ÊjÈf³Ù~¿ßw»WØÕzU[ªÉeb·N©>^/.ØÆ™jã5JﲊÅ:΢´ðâ5ÿÜöa¯÷Ã;ÿý~?;¹Xάc”bŒç¹¿NßÁîûºÉýÐh4V¹œ~¯J¥Raýn¼ïT&vß5H¼æAØòY1çû%MãË¡Ò4÷<s ‹öî@VÈj¥ÓétVWWWeÝXˆòˆ",ÕåeT%¦€¹ÅN=«Ã ã 'vÕêXdDX½ÿª¤Pñž…¼°Fˆôü[±FÄÉóoåÑ£Gdü^¢-ßÙÙÙO”Á%Mó·x:å)È:ðCVÈjG½^¯W«ÕjÏŠ+QQ‚%—Z…2s‹wƒ¤dÏê ‰Òxɵg=ÕÀ—Éd2 <÷²k­¨Jýžº”{ÏÌÌÌ´Ûí¶,åßÄ4ÄYù×´ÏïôÞ½{÷T µ—-ßæææ&µˆ·¦ ´ŒÒâ Yå%Yí(—Ëej+ðs¢>¢Âüüü¼—7U%…2êsë¿ÿû¿ÿ›·ž Íf³çVÔÆ+>dG+u»Ýî?ÿó?ÿ³¬ûG»¢k¼Ý€ffff¬Þ•ê‡ÈÌÝît:éééé æóù|œ÷&“V«ÕºsçÎU­V«õðáÇ¢ï[­V«¼¿ï_ A†zú ñ¬ö “¬NðôC‰aµ˜žJÄa ¨ŽçÊŠÞÔ¨Ï-YaxÝn·ûèÑ£G¢ï« Q¯`ø¨×ëuYkÔêêêjX•éUÁ®èÚììì,5'\×u}yyyyðoªxÿMdrQÝP¯×ëÅb±(ê~Íf³Y.—˼Ÿ¿4øÑÂÙQ,‹ÿöoÿöo~ïYÎ0ÊêD>ŸÏ‡²Ün·ÛW®\¹¢Ò&ÆCÆ€ÊXóíPÕ›å¹%# Ï0 ãÞ½{÷â¬Dy¼‚áãþýû÷yZƒ¹áÇË7¬Q‰D"aUæ½XXXXì²¢š÷_Ó>;öööö–ÐhµZ­ëׯ_W5’µ^¯×''''ýÊ×l6›ù|>ïç—¬yø‡Y?3̲:Q.—Ë"& †assssÓÓÓÓq9Ça ¨H¼©Qž["=†awîܹuƒŸq¯`¸èõz½Û·oßeðëe‹~£ì¼ÿª¶OEJd4ét:‰‰‰ UÓMùxÎØæ™È¯ò¯i6Mg¡0év»ÝÉÉÉI‡~È Yð3ÉX)‹Åd2™Œ£‡1 ,ÞÔ½½½=Õ IQž["Œq‰öñ".ã ½^¯7===í7Z©Z­VE´ã†µJ>% Àêý×4u#ˆdòÉçóù±±±1ÑAƒøqhÕëõúÈÿáµV5›ÍæÈÈȈÈ3‘­@Ó~:àÍÍÍÍñ”ÌÿÄÄÄ„ÌIÈ*‡(Éê†9Éü|AÌï4222we6.c@t]×oݺuËíšn·ÛÝØØØJ&¿DunµZ­V2™LR=qŒöq"Žã år¹Ì£˜ë<ÿöØu b‰°óþ«A„Ö¦ÑÆ4²(Ù¬È8§”Ëåòˆ n†È«W¯^M&“IÖgýøã?’4{z[[} âÔXÖ. ÀÎû"0L …‚è”—l6›}þüùsÊgÎÎÎÎ>}úôIÓ`©|ýõ×_»ý¿a†µ¯2añ `¥ÕjµÞ¾}ûvðo·nݺ5¨èÌÏÏÏ[½ÿˆ ÃF&“ÉœŸŸŸ{Õì`¡P(vwwwç ƒm{aI …B.—˹]óöíÛ·qï'¢Æ+€Š5 “Éd4ísÂÅÅÅÅÁëáýÃN©T*±ñÄ,¤Y«ÕjÔg†a¼~ýúµùo@õz½îÖÛÕ«¿+A‚ñ  b°¼¼¼¬ëº¾´´´”J¥Ræßáýà—X NxèÝ8>>>>:::2ÿý+1¢DÓjµZÉd2ÉóÙN§Ó™˜˜˜-Ó ëëëë³³³³fHr&“ÉüÓ?ýÓ?Y½ÿ¨@8X»n ÀÅÑÑÑÑññññàß~ÿûßÿ~Ðûú!„C»Ýnïììì þ ½^¯ç¥Ü£~Ác†±²²²2èý×4>ØÙÙÙi·Ûm»ÿƒ÷€pxøðáC;à nÜ¢àý xŠÅb±^¯×íþ€/œ¢¾ûî»ï€aÄ0 cnnnÎIù×4>éõz½½½½½Á¿Ù È¡Z­V“ÉdÒ+â€o6666ºÝn×ü·µýÃF½^¯ŒŒŒT«Õª¬g‹ÅâÈÈÈH¹\.³\À7N§³½½½­iðþ0H¹\.X˜›››3 àޫZ­VïãîÒH§ÓéÓÓÓÓB¡P[Hä›o¾ùF×u=l9Q#›Ífûý~ÿÂÞÐ#3tÉé¾F£!ú»À/#@¥R©ˆ¼” $ …BÁIa§(í^÷9<<"•J¥âGy÷úüéééi:Nù`A×uýðððG‰o4 ·Ïõûý~6›Í†ñ½`Á«xŸU™g1\\ð€$¼Šø™|ûí·ßz ..èE^ÅüXAÅ@q¼ŠúyŠÿ@Dð*îçŠþ‚¥( (ú ÃZ°ŽÉႵ; jÕ€ÂP¢“OOOOÓét:l™A°PêØ¡[( k}2(ÿà «uë@AX•:(ÿ@Ó0^†ŠB¡P`9ü™ OuaÍû“2G]ÃXÖ1j›÷¨¿OÖïK­p‰W j¡‚°r¢"'‹¼ª bÖü¥0#Ô…Aµ”Å’'쇷3Fˆš{Q°˜²††½á$jû&¨L:Nommm%‰„Ûu†a÷îÝ»×ét:"žI9ï©¢c€ŸS.—ËÍf³éu].—ËIÛ‹y,9NÈT¼¢"§ªü*Ž2¥ÈßY¦œnˆVȽÆk TeÅ ™+ïÝÏü§ÆÜ™oÆ3§ý®Ñ”߯£"ß§•°òùÂÜ7Yž­š±6â jíÝZ+ã¼AÁm]`5({ÝGôïuxxx¨ëºÎó¬¸ÁòùùmdœíDü~ˆ‡è1Ä`¾ìZ­Vó'þOär¹œ)°¨=*rF sQyÿþýûT*•â½O©T*±Ll/'€ÈßÙÄ”SöÆe.Š~ß«s¼Fí ȃy9???Ïd2¿÷«Õj5ÑïNÔüÑ4MK¥R©÷ïß¿{3K$‰ÝÝÝÝÁM'̃ÞÍ›7oÆas7 úýª°oîììì´Ûí¶Û5qûÃA¥R©är¹œÛ5†awîܹÓjµZ”ûšë¬è³¤¦iZ&“ÉœŸŸŸ‡}æŸÉçóy¯H€D"‘ØÚÚÚbù½\ æáµT*•¨‚R0 ¼ƒ,*rFB¡P½¨ÔjµÏaÖÌòZDEa.|¢#Ìï!Ãx1LT*•Š(Åß ïµR©T*26eÓ Rص9_Â0”¦R©ÔÒÒÒRÏ šÁ÷+êwWißìõz½õõõu·û Ãï `Ï•7Qi/´’Íf³-¤h¢ §)£é :GÉ,2W*•J³³³³,5Œ÷šËårQ)XÇÓb# ¥Õ –‚[ªÁ’G'‹L&“yüøñc¯ë”QõyDUTyVÅu]ŠÂ¾‰(@Ôñ: DYù7¡:G€¼Œnõ~fð WQ…(ȹ´´´4(£¨*ø^Ø™K$‰åååe¯Ï†õ^Ÿ>}ú4*wŠ’¡ª·­×ëõVVVV Ã0X®o·Ûí±±±±É”Ë岓 ËËËËaÖNð ¥Òu]_[[[ SÆåååe•7cÙF€T*•š™™™‘u•)•J%¯½% û¦¦±E SÍ@tðÊûƒòoÂêrñ289 þbÈf³ÙÙÙÙYIò # r¦Óéôâââ¢ÝÿÕjµšŒ.fK4§"s^Qa¾×(ytXeUÝÛÖjµZ››››,׆½È«0ç½BÌx:4›Í¦1¤Z­V©÷ ûwb'ІÂ×_ýµ¬{«Ž›H…9Ä K@&“É,,,,%°àeh}øðáÃ8(ÿ&Ãy§÷ïß¿ïÙ[*•JÖ³×_ ~+QÓêý·"º¥F£ÑhxµDóŠày¯†assssƒïplllŒ'¼\Ô"b'“èß™Åûä5¼ð«¼ïyO›‰—\&óóóó÷z½Þôôô´Ûïlekö”dí¹é·÷f£Ñh8ÝÓÍ;9yd”Ïôä³<ÓÌóg¥ßï÷íÇ<2{/¨²]\¸ÿNN}a¾+n¿±^Ïä§~"C¬ï›5Õ„ò;…QÐÒm>8ÁjX5¨=‹)¿3Ï÷·ûí+•JÅï÷ä¹—Nc‰gݰB]ï)óÕ¼·È÷éç·¶b÷^£°oRïc¢rÝ PçCÜßÔñï4_Y‹0~Ê9ÆÏ™‰ºFñ:¹¨{jÐ5¶ÂÆmLûy¼{»ÛïL=c{ÍQóü_ÀÕ«W¯&“É$åƒÅb±è–³ëF>ŸÏŒÐÃ\£ g§ÓéÌÌÌÌP<"Ôúvyþ^¸åQ½Õív»}ûöíÛ½^¯çtM½^¯S_Š÷²X,GFFF’ÉdÒO8U>ŸÏ‹Å"å3‰D"qùòåËNÿÏãý¯V«Õ‰‰‰ ^+e½^¯ŒÐ#êõzÕúF¡¹ÑÑÑÑñññqÖëYƦI½^¯S~{§4j„Âêêê*ëïL­× iòÃàËår™g¬ "s,E! ÂŽA >uM2¹yóæM«‚…}ÓŽõõõuD¿ÌÌÌÌPöãããã㣣£#™2 ’jµZuõò2R.—Ë<{Ïøøøøèèè¨õïét:}óæÍ›¬÷i·Ûí·k¨ûÙÛ·o߯)åB8T«hOà …ÈÍC9M¨VÌ‹‹ÏÖªn·Û¥|Æ ·ƒ6Õ¢K±¦‰¼w6›ÍþéOú“¬ÃÕJédeõ”q**mE†l¼–^Êoo·¦P>Ïûþ(ϰ[›dx¬Mx½övïRDU~U"¬ðì¿\o:}–õûÆ Døƒ'ªÆi?F"T!N§eåÞóDØ{êïÈ2w‚Ø{‡…KšF˧0 ÃX___)D>ŸÏ³äFEN“V«ÕJ&“IJÎq"‘HP<žn˜VA·œ£7nܘšššb½'Å:Þëõz”ßÀ­ÐZ«ÕjýÝßýÝß±xvy`ñ6 â4©Þ†f³Ùôc±Ê/_¾|™Õ»Îkéýî»ï¾£Köª…{ooog Sd º>5’ÂDV¾>k×ÕiµZ­‡>¤~Κsµ}s–uy˜ ?w¨ç›n·ÛÝØØØ)~ét:éééiYyí”úPnP¢#Y¼ÿº®ë·nݺÅúüÍÍÍMxÿ¹ä}ÉÏé÷ûý>|-H§ÓéLLLLx VT’Ó4ð†uR1‹~°(–KÓ>È( Ìþþþ>e! +|÷èèèèøøøØï}(‡Ñn·Û}ôèÑ#¿ÏÁ“'Ož°†s‡YÐüñGžÏQÇè ”p4Ã0Œ×¯_¿æyUFjá¿PRIL¼:+øÁ.>ŠðŒMj¤ATÚ75íóÞùöíÛ·nר\øÑ+º$Œº*N¸yÖ¢êI£¦g C±0 ¿…*À҉Ŋݙƒ²±è””P󼹤ëºî–ÏlåììììÓ§OŸd ô»ßýîwv pTätÃÌÓö›é„ٯݩ²¸T…Õ«:§•N§Ó9888`½Þ)—H6½^¯wrrrâçTo0%\6”h U=¬ßÿý÷<ŸûôéÓ§³³³3žÏR h~”+ªŒaÒ=zôˆª¬Ê’3è(YP×OMû<&Íw‡}Ó+úEµõh0Lúýû÷ïÝ"Âr¹\îâÂ_X? †è–J¥’Óu‰D"±»»»ë¤´PSß‚P|ÜÚ1;á' ¸C Ývjgí„YG‹¿iÖ0÷\.—cý¬9ÿƒœ¬ðž§L(ça₩Ñ"l····U9_« 9øÇ,¢å§õÆ ¦â?===MñÎu8¤xf“ÉdòêÕ«W©Ï»ïDYœxŒ)²Q½ `Ôñ£\Q TaÒx¼SSSS7nܸ!Cž¸„†óF¶Ä…¯è$U¢*•JÅ«%¯fkà r™M#UÑÒ´Ïk"¨éx¬Jšö“Aƒg91hû|urrrÂÓ.Ù„2ÿX#‹Y#áýgƒlËCK% ræóù¼ŸJÚf/qªâoB­°Î›¿ÌÒÓÙDfX°H쬣”°kUC )^Ü *pûÝ„dCùÍONNNdÕ°°–!2×5Í»£††aüãÿèv*J¡_¨ž?{ŸŠû&KtRØQ¦BíæEg¡T*•d{³ÙlöãÇ©FŠÄ€°•7¨@Þó .L¿ß¹îE­V«E¹¸!ëÙˆÕðFÉÿ‡÷ŸKTÏR*•J----ÉÊŽ¨È%¨í4xC‚>|øð¡ßï÷Y¯÷“ÃÊ %\É0 Ãn,RäV5ÔâÅ ¢ eìðæ½S aazc)ÏæU¬ýÂSKƒ×Sÿç?ÿùÏnƪ¨e—}“% ¬ºº®ëoÞ¼yãG¡$—Ëådü³ÙlöÕ«W¯Dy,kµZME#@6›ÍÎÎÎβ^ï§> *•JE¤Ç_e¨õÁõвÎêýg=§åýO§ÓiF ]×õù—ù—0jŸq¥,...F¡À’êr6O¸ ‰6È›ÃD™àNJ/ ÔüåÁÖ XZZZb W²ë„@I§P1ü• RƯáˆ5T-j‡Ä  j_- ÞùÎ’VQÑ(£â¾É–ñâÅ‹/D)ÿ&¥R©$Z±N§Óé­­­-ÑÊËÓ§OŸª6^¨ÅÿÐ+xÑh4²½þ*A9?XõVe’vÃzN ÂûŸÍf³ïÞ½{GI1²#N§øá‡¦§§§EÉFᒦѽZ©T*µ¿¿¿ô¢9½0s‡(ÅBÜÈd2™óóósjè`P ‚ˆ{2Éf³Ù<`½Þ.TâEvËg)”#;D´×ëõVVVVXB¹e‡Þöz½ÞÞÞÞ˵¼!߬hJ LP aDÒh=ºÅOعW›¸L&“YXXX๷*P×iëú—}“¥#BÐÆ‹B¡Pµ[©X뺮¿|ùò¥ß«©T*õ‡?üá¢ïË µU˜¦©‘Ô Ñh4dÍsU¡œ¬g#eÝ0 ceee…5í†e Âû_( "¢@DüpIÓøB»ƒ.Z£iёӉB¡P¸¸p¯¶ë³€ëw¥LpYí¡ì2•ÒÕn·ÛOž¢Œ~¹¤iþú`›í7‚8(DEN+fчßÕo{(•: nÎTcŒS¾5_JÓ~jäg\P>XoM“J­KðæÍ›7,‡ªJ¥Ra}÷",Ë~ [ÔŽšžÁZóÃO½–‘°rÃE@=ˆiÚ/cQÝ7í`Y“‚Š ¤… O;¼(C5ø‰VjµZ5µÓŒÞ¼¸¸¸ˆBg…aƒ¨kÛ` ‹²n†ÁÚvZÓØ"lezÿMA„!H”A—4¯×°ó  Ó¢9MLbé1 ÃpÊyvû?;ÌïjçåQ(TgГîoÝ…jµZ­×ëu»ÿ£„éþøã?Šÿ)•J%ÑcºÓétVWWWY®•íy£Ô%ðJ‰á©â»ºººê÷èǰEíØ1LxuH¥R©™™™™ eµ•™¦ýÒØµ}Ó U¢¢¤TGÅP!ªÁL¥º.fg§3›ŒçPñS³*j躮¯­­­…í¥ êÚfÍãg9«Pkn°ìƒ"Îhv˜yú~ǽH#‚(þRÐ+—’•A‹¦Œðä(ÈiþÐïß¿Ϻñ†aÌÍÍÍ]¹r劓‡£ßï÷ÿöoÿöo'''')Z­V ÛºL C «‚¹Åb±X.—Ë"îõ×ý×-²³I&“ÉüðÃ?ˆô~Õëõz³Ùl²\+³ `¯×ëݽ{÷.eܦJ B ½r3üPÇ5oUzE0¬–nÔ¢Ÿšæ¯I«Õj½}ûö­Û5A´¬M6›Í>þü9õsvc2 û&+,Q²‹?òÌG“v»Ý€º§³âÇPažIF,°îa@5v¨Zü¯V«ÕÂ6¶‰¦\.—­cɉ¹¹¹9ÊzU­V«¬÷¶âÔF›šJÂ#—¬yï‡gÏž=£Ì!kTl§ÓéLLLL¸}ï|>Ÿ§ÈT¯×ë^¿£ÓÍ¢‹ý©f<û‹€åEeð.Ê¢©ºœ~üøiÁ£ëºÎ{–1ÏŒ####^N£AeylllŒ5ÊQÔ¢¦”*þQ#NÅþœøYÀG=’µ¨×jµš¨B•å¤ä+7›Í&Ë"á„i«V«U–ëEZ»Í(kµYDJ*•J½{÷î¯Â ¢œf 5¿yþ¼ ÖPµÒm(•J%ÙnÅ¡4í§ß’z`—‘÷ÌšG,«  ¹ÀÊΫ2 œ±ä^²(ž^ϱ""mØ`‰R¹yóæÍ8zÑšÍf“ZYÅ}“Š—ò)z¢¶Ïõãiæ1,BTØÛÛÛã9ÔûYÿDÀS°ÕX¦²kìú¼‰òùºR©T¨mé>|ø0ªß×Jž~·Ûí^¿~ýºÝ÷ç1"Èè\@ÁÕ iŸ­ìÉd2)º:­k•z**ÊY¯×ëaZw4ís}Ñ2°>³ö£Ž*fD€ŒhÖâPšöùÀN5ˆn‡EñL‰>¬„ÑJ¥V«Õ¼~wJz‹W1À ÒS‚¤Ùl6y×x÷M ,ʧÈH(j÷œ~¿ßÿðáÞgQ ŒZ¡D*ø1Tð¤A‰D•âÅb±èÕªL„Á-Š-M£5ÂGåNnT*• ÕÑP,‹a+­"¡äé»x¤ÚívûÊ•+WT0¢xLL/¤Ìƒ‚ˆCBTäŒ2N§spppàvß;Jõ (•J%ÑFj!j%o!°aL§Óéýýýý0Z©°üî;;;;a·ð±âGùˆ*,k“ìñAâGù$ªû&‹ñMd5¯Þ­+ƒ,cÙ j¤‚ßµ"Ìýz~~~ž æç7ÄÚãE12 n”îMVd·¸f¨ó& $ìàQþ«Õj5NÊ?%Oß­À#ÕˆV¾¿Ìó  «w"‘H¬­­­ù= EEΨrÿþýûn OaBæoî…Ÿ6+¥R©$Ê«ÍSŠššH$” …^¯×[YYYañdˆð¸êº®¿|ùò%UùwòÈð¬ ^¿;å¹xyddÅÂ@Fþ`÷Mã›èz(¬ø‚ãý<5RáììììÓ§OŸxž¥išvrrrÆÚ—N§Ó‹‹‹‹¬×û­‡4X§ÇoZg¹\.óÎ3?µL»QQ¡W½L‚ž7aP( <ÊÐi̲ äé{µƒ¦ÂÎ÷·ƒl01 ù±d:!ÒÂ9£†ÙuÀúN͇*[ MÙy=^¢¼Ú¼žj…h(ÕŸý|üøñcJØ¿©ø;AsM ¾¼~÷V«ÕÚÜÜÜd½_\¡zQ4Mlº—B(«>EP˜žG™kl”öÍ £¨…õ¢ÂÉÉɉ¯”ßt^‚,þW¯×뢽wN§síÚµkT¥:®µL†áãwÞM¡P(P[Ó5›Íf\”MÓ´………–ó¤[¾¿¦±w‹P%ßßnÀ år¹,Ú:(#Ï)*rµùåË—/Û}OóšÈhq(“z½^§*‚¢òˆysǨ¿¬n AÌf³Ù<`¹–ºÐR«¡³üîår¹,ZaäüãY=mQôNˆ€%'9jh‘žG*QØ7Y¢ÂøÍý†Æ«˜ §TO8o¡C™ôz½ÞÝ»wïRÎ Éd2yõêÕ«2åÞD©Î¯ò¯š×:Üòý)xÂFˆÀdÐ³ê·Ø‰LïzTäŒ/´aÆÉÉɉLyét:{÷îÝ£Œk1u#ðó©E—œŒ7~1;]°\Ë[pyyy™ÕãÆ›vâ•Î2Èââ⢗!ËP,‹ÿú¯ÿú¯¢ï+j¥¦‰Wt¼*¨§R©ÔÌÌÌŒÈgŠÄš¾¢BÞ Êû&K@ @3Ùl6;;;;Ëz=OÊ]Pð¤ö‰îðâK6›Í>þü9å3êü»åûSeD‰PÀ f±?¹„AôlVMÎÁC–‘ƒ©×ëõ‚T²U§Õjµ>|øõz¿ظhc5ìð/²ÙlöÕ«W¯(s¥Ýn·ïß¿_¢XÊR*•JF£á÷>¹\.wzzzªò~#Í`B ³D–çÒŽ¨Èããã㣣££¼Ÿ§lVa)Ç,ÈA•ß C5)ÏóûÛ9A)ÀÄÓ+—’ßé·¥Š?«â"¢ÅZµZ­&“ɤª!dvPó(eEüx‰° ÃÅ •öM/*¢â®ëú­[·n±^ï·ø_øéú€¼ÊÿíÛ·o«ó@ÓÄ)ï©T*õîÝ»wªž3¤L(a¶&aTkŽŠœ" „­™sVî2u4jP« Ç!?ûÙ³gÏXtÞÃktˆV<”´ j.ñ`w –Ô€f³Ù4¯·æ{SRiÂÊO¤J“eðó2è!ŒVªì›,^TDü¿ÆªÑÏ/¬…¼Lüÿ Êš= Ž'ÀO:NommmAùÿ9¬Î/åU_H$‰ÝÝÝ]QÝÃD˜€§Ð‰Œf^DEN嵿© Oò0oÀ”üËÍÍÍM6kÔˆ(c ë|ðc ³Í´Ã)ÿŒêõ «€5´[–1Œeƒ¶Öò|¨²o²FUÿÁoÞÏS Åú º;BŠÿè£jF:NïïïïS:d ƒò¯i?¥Y³D­™Ê{¥R©Øý>ŸÏ³Ö{ªÕj5©" Ì iÑ qŠŠœ~¡x­ýÖ¨EÁÂÌ]¦¼“ÁCµ ’¬°|;D+Xº®ëkkkk,>ÞÂKQ2e]HÓ„çw“9ß××××Ýæ3ЏŠC•}“% €· AXýîeã7ÂOF«Y'(µa4Míâ@-¨ç8@PþÙ (ïnu(õžr¹\îðððP•1¨@Ó蹺An,ƒDEN?P;Þ¢MÔðÀ(µV1¡z\üæx£„_nooo«\x)JPj"„òzãÆSSSS”Ïø­ßàÆÑÑÑÑñññ±Û5¿ùÍo~#ëùÆ û&K@P†¿BÞÏS‹üúI‡¡F&ùeiii‰¢Ü¨^ü¨CБ3¢òO£\.—Yk4¹ÕhµZ­ëׯ_g‰€Ëd2™?~T¡.@à ÔÅŽ×ÚI©Ök†ñúõë×Ôg„ õÀÕ>¾ét:ýôéÓ§,×¶Ûíö“'OžÈ–iX àŠ¢¡´mÔ4ù‘ ,µ2™L†r`êãÕRÓø¢¨Fsj½A¨žn+TC:o: ÑcƒL£h(Ÿaîò¢ *µ“Õu]ùòåK(ÿ4êõzµ›[]€N§Ó¹víÚ5–úªÔPÞV+•¨È9UiåµvF¡€‰Ÿ\FÊËOJõ$ò @ñ¾¬¯¯¯±±ˆ²ÂÕŽjÏë0½< K‘ ÔöZ 8dí›,m y¢¨Fs?†^¿…õ¨ï–·(#Õè窱¡Ýn·wvvvdÊ$ êúÅHIÕ¡6QáÜ ëºþæÍ›7”˜Ýn·{÷îݻìü›ð(ïvu(õ4-üº—4MÓ¾ýöÛoUGð"*rF Š¢Àcí¤njaWÇ÷³˜³xD<‹ZSAÔAÒö¯Ùl6ýô2§§DXáU¯9@9d‹èŠÀkWˆA‚(Î¥JnzX ë¾éUÿAÓèQA†ÖSûÜ[¡Ö+H$‰µµµ5Êû &ýB56D©ø%ÅKÓ¢étŠÔ÷v;Y^åfff©1?AUÞK¥RIDqÀ0ë\Ò´Ÿ,"úzAQz¬á¡Q‘3JP ÔuS sæf¬ÆŠ ÂC©9¢ ²ÛþùÁ¯žR× è(•B¡PÈår9ÖëÃÈy¥Ê¨iÁ¦ûD) X4úo²Dðx½©Už¶ƒc«,õ/¬d2™Ìãdz\ËÓfÌTgB”Ò u]×————Y¯ò™S"«ñ{µµ’H$”ßN4/^¼xå_åÝ J}°øY @*•J½ÿþ½,k„(kqTäŒÔC%l’gS³nØét:}xxxDßfªwÁZOõ4¦R©ÔÒÒÒEFêÁP”7˜2'Þ¾}û–§íŸÊA;—Ëåxó©(u 4-Ø(•l6›}þüùsÊg‚Vv+•J¥V«Õ¨Ÿ5NX`íýg†qßôŠà9¼S ª<ë<ÕÈkO8³¦¹{¶LxŠù…êLµ¾ …‚ì0]ªvª¤l¬ÆÏ1–Ëår^s†J6›Í~ûí·ßº]Óh4Ã;”6D)ïõz½ÎÚ! 4*•J呃t:>===µ{ŽÖƒJTäŒNïÕï÷q_ëï!«hF£ÑhPd½¸¸¸°[ð …Br~¿ß§„Qå16u]×Yž'r.f³Ùl¿ßïËz—ÔïfâtÔu]ÿÓŸþô'Qá€Ôïqáü{Sæ"ë{äyw^Ï`?<¿5u=²Ãœó2Þ§,†yßd£Ô߇ç»RžAÝC±î5  ^¯×e(¦¡äüüü\ä}ëõz]D«I³ÙlRöD*óóóó,ûI\R2‰Ê:/Yó‹J>ŸÏ« P‡N§Óq&íõz½üÇüÇ0ΗÊÂ>°9ýP.—˲•-ŠjP•Ñ.7D)ª ™L&ÃrmañAŒK+ª¾ÚívûÚµk×T“kf³ÙQ-§×ëõöööö–c˜Peßd Û§Dhš¦Ý¿ÿ¾W}*2×Õz½^Ÿœœœôë½’Õ£ëº~ëÖ­[¬×†aˆJ™P…(¬ó²‘1¿xÈçóù°œÈà’¦…s°¤Ýn·¯\¹rÅkÃ‹Šœq \.—e)®Íf³9===å¿Ûív''''ýx9‚(ø!ꠠ뺾¼¼¼ÌrmÅeŽK+Íf³9111¡Úá«Z­VUž?¦â¯’×ßJØ5:‚ûæO°FÌÌḬ̀޳×ëõnß¾}[”’"Û«®iŸßÃÄÄÄÏZj†1777'{~S Ñš¦iÇÇÇÇGGGG2e ’b±XTyŠ^¯×›žžžVÁ_.—Ë*o@ A¶·ñÓŽ$*rFÑï™·U\­¬d´µ“ÑŽI¤œ”÷vÛY­­xç»ì¶A~Ú:Êž?¢ÚØÈn8Ïø‰bÀA°o²½ÞñìwM²®©Ô–_~ö¯1í¶þPÇËZF}—2ök“ [Âñì«ql(â»Ê”Qöžê4Ђ¬EODò(ÊeüE.2{ÃÕSÄ&&úÀCùÃê[j‡¨XTorÑãSÄï,ú°"ë°¤€ç uÀ üo²¬¿¼cœg¯tZ{‚4øºæy¨÷µv³"z=õ»F “`M¿2ŠTÈYd„Ïà ã@9£ë¢ä‚À{˜UA‘e«²9”CDXM/¨‚ ¾ulba1,û&‹‚)boðzŸq94SÇM\¾· Ïù#nï¨g  0œßQ< 0LP<.˜Ï€¨#3 `Ø zÄñ^ f8Y‚±à .¬ÞUÙ€BPQq‡êõÄ1ÅN™À¢€šPÂ7áýÄ/Ã'Î-ÞPÃÿƒÎ×N–u(¨Å{ƒƒ N°DÄåÜR( ¢#x ž"ªbŠ“Rk:j¥¶ h\Î-VO½ßõœ·9öˆ)n›*Ô€R1Þ@aQdã ª#Ÿ¶q1¦°Ám£‰ÃF @ˆCÛ?Þ¨Ð?áÿ1ÌÍÃk÷R*°>”ÜMÌYˆ6*`H5øõ™L&s~~~.C@0,///'‰˵AÍyÃ0Œ;wîÜiµZ-ÙÏív»½³³³¶4íRÐ<999éõz½ Ÿ øL¡P(är¹\Ørˆ?†a++++8û€nøî»ï¾ ú™€Ï躮////‡-€áàáÇÙê¨!`„ËèèèèøøøxØrˆ?Åb±X¯×ëaËà'È5xA@üAMP—@"ÚívûÊ•+W°Ä—jµZM&“Iœù F°¶CËÔ"N§OOOOƒhùD¥ßï÷³Ùl6ìwq¥R©Td­á8ó( auêX©T*•°e„š»Ü<àD£Ñh ×S…B¡@y·<…³‚x t]×Yú(¤ÜðSZ´Ébˆ8<<<Ôu]·~–uNm¸ Ꜿ+”uáôôô4N§E~W€X•6òT¯±ÑÆ¥¸( ¹_å?Ìg$ÁÚžÊ?pƒ·Í“Ìq7«¡N´"N1 •Ea '†òÜ`Uÿó?ÿó?Y®¥ÀÆÉÀj`i4 Y2°¼O¤ (,z(ÿÀ –q4¨(³\/B‘‹€ÕP'Sù7Áš@a 'FN/ð‚eÙ÷ãý…¸‚2˜DU€ ,áÄPþ^Eÿ¼ÂÁ½Æ¡ßpò8X +†¡l³¼[´ dX(ÿÀ /å5 Ü+¼ÝO8yÔ ,†º0‹îyEq !ã¥Aù^x)íÔ1ä¥lóŽÉ(Xž¯BÅ}/#ÖBÂ+œy»€7ÅÚÏ}ß(¼k”“(É ÀÐðÿþßÿûaË¢M:NÏÍÍÍýÜ_ÿú׿O²®ëú7ß|óMØrˆâ›o¾ùfX~;êá•Rç§f,§ˆnãËO:¤—3 ã lp‹‚åUÒÜ”¤À nã‹·žÆpâ¦PQ•4·ô7Á 'ï;%ýЩ¶ÕÃŽñŠ ªˆ±WM-Ô¾‘k­&%^”ã‹KŸkjnŽWñ/?­Å€;,­Lc©ÐÊV IDATw¨U–¢¸"Ïá*ã5.½”4¿Ÿb Œi/¹ÓoÊ“cï·àúPl÷²”\\Еu– GæË£ È0TÕå «…0hâðn@%†mSƒúî‡m¾xu¯qÒ¼Þ+B³ƒÁë÷£þ®N÷ã]“¼ö"§ûz/l/Y¥èÌ,? Õ’Ær¹ÉðB+²¢dËÇbÄ$hË þ5~D2¸&@>:Q’VE0H…ƒ÷@îGƸþ¾¢¿ŸèýWÅñ0õLÆJTÎ,¬°œ ­ã‘åÝ:a¿2ˆê|1Ædz›ÇwÜóŒ–ψš‡¼çÊó/9ýG:N/...zÝ`ooo¯×ëõXV( ¹\.çvaÆëׯ_³ÜωÁ…£T*•üÜË$•J¥Þ¿ÿ^Ĭº|w¬‡ŠÝÝÝÝD"‘[®¨ƒý {²ÙlöÝ»wïR©T*lYT§×ëõVVVV Ã0œ®)•J%s=Ðu]ùòåK·wÛl6›år¹,C^ð3333~Çx­V«™{s­V«Ù]Óív»ûûûû<÷ït:{÷îÝc_ét:½¿¿¿ï5¾êõzGsÿä=Ôjµ«ÁÜѰ´´´Äò~ÿý÷ß³¥ëº¾¼¼¼ìu]¿ßïøðáË=í¨T*•óóóóL&“὇µZ­Æ{Q]>ˆ#Vo´Ó¡ðƒý {Òétzkkk FFvZ­VksssÓís=xñâÅ ·u§Ûív=zôH¼”ÀÊ×_ýuÏ9;;;ûôéÓ'ÞϳŒ¯çÏŸ?ÿû¿ÿû¿÷2.‰_Ùl6ûñãÇ~÷ÏT*•z÷îÝ;¯T[«÷Ÿb}YXXX`ùRN§ÃrÏAÌÐ QѨ.ÄSñÇÚ+ìo¸ÃêT?çÉ“'OÚívÛíšZ­VóŠ.^]]]åÑ- ]×õË—/_¶û¿f³Ùù?&'''»Ýn×ϳÖ×××Y#Ðð_‰D"ñïÿþïÿú_¢ „‰D"ñêÕ«WnF[ëBÅj}aõþkš¦ýøã?²\7ˆêaUªËð€ý wXjà—°¤x!"4°1::::>>>ný»ÕCÞét:Åb±Èóœv»ÝÞÙÙÙñ#«¦©1¾XÒWxH$‰­­­-§t€_( kþ?«÷Ÿ'ÿ?›Íf_½zõJÕ°*ÕåxÀþ€7"r¢‡–Pm'ú,W¯^½šL&“Ö¿;Ew×ëõúÈÈÈH³Ùl²>Ã0 ceeeů÷ß$ìñõøñãÇ²ÒæR©TêÙ³gÏìþïJ˜Kþ?ÅûOÍÿWýð¡º|@>,•lcÑ30T`€ r¢ã K*€ýW¯èî|>ŸŸ›››óòĆaܹsçN«Õj‰”/¬ñ•Íf³eêýgÍÿ§Þ×JµZ­zýxƒÖ§jµZ¥Ü_uù€*•JÅï¡ØJ­V«U*•ŠÈ{úM£½0B¾x˧2f‹"ÞªÄAöï‹ý ,‡üANNNN⸾Ɖn·Ûœœœ”½Ëöxƒpñ ÿgMµ×4MÛÙÙÙñŠøòË/¿ü÷%M£{ÿY”ujUCÖüJTÁ fNuB•ËåòØØØkqÕåâámñcÍó³;ì–J¥"Ï`Oâ‘‘‘‘|>Ÿ[&ÕÁþ€\xZa¢Ç_|ñ…ÛÿSêâõz½ÞÞÞÞžÛ5·nݺ¥ëºnþû’¦Ñ¼ÿ,Bñ(D,‹¯÷ÁoNU¯×ëMOOO{y#T—Èjá×´Ïž|øPDA•r¹\vk» º| |ÌÜW§‚(Öü×D"‘X[[[´ÜØ0súNéìo€XŠƒRú“““·ýq||||tttÔü÷%§ U„© iâÛ€´Z­Ö;wîØ½dÕår8::::>>>öº®X,½ò§;NçàààÀú÷L&“YXXXð++ð€ý ¸¡ëº~xxxxáºÛ˜-–Yh4°åì` ù Ã0Œ“““™ÏˆŒ€'RAVV«Õúÿøÿü›êòyxõ5sYÂN6ËËËˈ ö7àD6›Íöûýþùùù¹[}³»ÍÅÅÅ…èâ¶A“N§Ó§§§§,Jèîîî.ÅpV*•J¬ î ƒµ‚*•J…ç²ðSÇÈ_æ½(Å–s¹\nPÄcý}¼°›û2×8ÍÑDÆÀ“gM) àÕår1{Ÿ6›Í¦ù7³b75ר)ìQ@5(áAXq<‡áÃÃÃCÊÄ¢êþ†ñ.F£AUp5íó{¦÷蘊ÏørbÐ  ’"FE×uýOúÓŸ‚¨ %ûYXCÂ#2¯v V Ã0¼ú"ŠDuù@0äóù¼YdŒ7ôÕ-Q€ Áþ1Cuý´¾Íd2™~øá‡a él˜ÞàR©T’ùm–ƒ@×uýÍ›7o¾úꫯ^½zõJ¦@æ³°†¸“H$^Eý€W»+ÇÇÇÇGGGG²ä±¢º| LMMMݸqãFØr†ìo`/^¼àii%•J¥^¾|ùÒΨýÛßþö·"ŽÂÿá÷>@>•J¥"Òã7L…Üœ{‰D"±µµµ%C¶{–H#@kˆÊ|úôéÓÙÙÙ™È{^¾|ù²ÛÜ9;;;ûôéÓ'óß‘0ðäžœœœ¸[‰êòøH$óóóóa˰¿A …BÁ×ÎJ&“É<~üø±õï‰D"±»»»+Byª·w˜h4 Ù^ÿ(cUÈMR©Tj_¤ÀéY¢Œ2Ö¨¥Çöz½žW‘¿¯¿þúkÊ=¿üòË/)×GÂÀ“øÝwß}'K+ªËâÕ¼`&££££ËËËˢﻸ¸¸è¤ÀÔjµšˆâm¥R©„"pjÒh4"¸ᤛˆ4x=˯@ÖòôéÓ§QKp*ømróæÍ›¬ßI×uýÖ­[·Ü®ÙÛÛÛ4ÌGÂàÖ`%ˆö ƒ¨.ˆ”Eü€ý ˜|ñÅ_LMMM‰¾o*•JÍÌÌÌ8ý.—ˉ(ø%ê>@•J¥åß………¯py'#€[|kTŒ—òo’H$¼J|XkˆŠ8ü6¡|'–1b}^$ Ô°†~¿ßÿðáÃYòXQ]>/’ÉdòêÕ«WÖ°¿“¯¾úê+YùÙ^ᮢ ~ŹpXÔÈf³Ù<[Õ©×ëõb±XôºÎ̇ÿõ¯ýëÃÃÃË‹‹ ·´ ³ËÉéééé_ýÕ_ý‹ò¯išV­V«ÔîV&a®!ª±¿¿¿ßív»n×°þÖu]÷2Èt»Ýîþþþþàß"a b-t ªËÂÅËãæ§:h­V«QÛZ±"*WòÅW>à(ü¾Øß,‘m©T*õîÝ»w~sEÝð£ëº¾¶¶¶Å‚ƒýèƒZ‹Y™L&óã?þH)°—J¥Rÿõ_ÿõ_¬Ê¹\.³Þ;H¢Ûét:ÛÛÛÛn×d2™Ì‹/^¸]ÃRPÑ®-ï¥N§Ó™˜˜˜!àõã×ëõ:å~^-Ó¨9ÏA R]>?¨^9àûp¢Ýn·ÇÆÆÆÏr““““^^-;XÃ]EôsʹynnnΩ­¯ÕjµJ=?[ÏÐår¹Ìs/XP+vžGMc Yöû®xÇ¢ª°d!Cù¹†ØEǪ>^¿~ýÚk}Èår¹~¿ß·,Í–™^)4Nmyc@”r¢yddd¤Ùl6YîÏsžžžž†:`ü…GµZ­Ú}Wó7a}ǃPö>Q•ýÑ!€l6›}þüùsÊg Ã0îÝ»wÏêyd Yv¢Ûív''''GF¼‘ƒëÃØØØX»Ýnóˆ›R:HPk+2悦}“++++¢Ç£SÔ ø P(....xÚØ”J¥’5"`tttt|||\¬”€¶···Y=°,ù­ƒ\¾|ù²©ÈSB3™LæãÇíŠúÕëõ:«²‡â€?G×uýåË—/© OµZ­:)<ét:}óæÍ›”ûµÛíö“'OžP>ãE«Õj}ûí·ßмgаzŽ‹ÅbÑšªÄ’gï”;î— Ö‘Ș ƒ´Z­Ö;wîˆ2†aܹsçŽSÔ¦Á7F£Q«Õj^×YóHCМz·*a†ñúõë׬×øðáC¿ßïó>¯^¯×Y ‚¹õkµZ­ëׯ_÷{Ÿaƒ¥º¸¯Ðî™™™Še†±²²²2 u5¨x¥R4›ÍæÈˆ}‘u3ÏÞÍÈ–H$¼µœz …Œ¹`…²N¹Ñív»×¯_¿î¦ük Ñu]?<<<ôòú›Åj¬ €‚f†o™½[u]ׯ^½z5™L&eÊPé÷ûý>|`½þÓ§OŸÎÎÎÎü<³Óét®]»v5GÖ©¨µ°Ø°l4 jd#K¸8µÈ±S³aÇ«"kÊ„W¤èÖzÔ5DdÍ;X 3n‹Å"k  Âb ìv»Ý™™™·IX.—Ëæa$“Éd?~üx~~~>Š}qÄ›³³³³OŸ>} ú¹V£¹nÅóù|^Ä}âL¥R©Pžv»Ý¾ÿþ}¯ë¨EÚ¾ûî»ï(× 7nܸ1555e÷Ô” ·¢‚¬í9Y¡®!"Œˆ~9ܨ×ëuÖ®5f¤‡S´‡0@€u1X]]]e±À­¯¯¯›9?Óív»wïÞ½ë56¨EŽíú¦ƒÏ\¾|ù²“³hooo:OÝríE¶¦{ ¡ s.PÈçóy·Ö³¼Ea€‘l6›}ðàÁ¯ëšÍf“Õ wtttt|||¬iŸó­d´+# Šåò §|Àø}Á°C)êçVÙ¿Õjµ®\¹r…%µ`X:ˆìonµÈqX'^˜éa®ÅNJ95ÇÞÄ-×~ÛNËž * ¼ ×ëõNNNN(Ÿ r°ª.ƒ®ëúÚÚÚšWx>µjj¯×ëííííQåA¯m€l°¿ATÙw(Ê{*•J----ÙýŸ™ZÀRÀí>q ›Íf_½zõŠ’‚ÈRi|j£(y‹£Ž[¨½ÈÊúª¬!n1T@y*àUmÕ„§`Í÷ßÿ=¿dÀpAQÞ½ÈçóyÞ¢[q N§·¶¶¶¨õ‡>|øP¦ÂeQ5‰D‚’faâ1L†Uç‚ "a .²ú@:¡º|À?_ýõ×,×ñ¬9999¡öþ„ÑØß€ÊˆRÞ)©qBvs ·3 OÎ>:P?²Ùl¶ßï÷/ÂÚÚ4*ããã㣣££aËá„êòŸ“Íf³³³³³^×U°Æ0 ƒ– A€ý I£ÑhÔjµšßû …Âîîîî°uá ¢¿9›ãhqqq‘Z¯byyyÙiìK'†a› ‘0P½Éd2yõêÕ«²ä±¢º|ÀnÕV9888¢øGû¨¢ ö7 "º®ë‡‡‡‡Ô]vˆ2"D û›± ¶’J¥RÏž={Æz¯B¡PpÃÒ‰açB$ ÔiÞ^T—øCfø?ªVÆÄìo@5²ÙlöãÇY¼uÝn·»±±±a÷#‚Û}¢HXýÍz½^Ï­àt.—Ë5†×} …BÁÍøµ½½½•ªö¼ ë\ˆ„À­=…¬J›T—ðÃÚ·6Ȱüa*Èìo@%(¡úív»}íÚµkv ňàvŸ(¢Js/ÐQÄ·N¹\.g—û­iŸ‹Ýžžžº)ÿq3zÙ•¹ ƒH:Nçààà€ò™ ©.à‡µomaùÃ’ìo@*•J…5T¿Ùl6§§§§íé•J¥ÂjDp»OñòøÚ!ª¿9Õ˜ˆuÄÇ?f1`Õjµšµ Üû÷ïß{»£¦D0ç‚ D iôJÄSSSS7nܸ!K+ªËø`­Œê',ŸµÆ€¦ O>@°¿01CõY=uÕjµê”›Ûh4"îE²ÙlöùóçÏ)ŸÙßÜ­×¼((êL¥R©P=×<°¦D°ç‚ DÆðúõë×Ô<Äåååe™2 ¢º|@.A…å#ÿ¨„FHmGÚoW*•JÔ{Âk$ìo ,Òétú‡~øµ:w±X,ÚUå6×*ÖµÇé>Q%ŠýÍS©Tjfff&Œg«LPÊ¿IÜŒªÌ…Vëÿ·w¡m,yâÇ[Ã%|–}‰e29å%²aaí"ç"A’a DÊa™@rˆÉ`_–Ç\ˆ48‡Ì\, 3 C)øËdYŸf°å$§Œ×Ê»Øíƒuðªïþ2ýzzêîªV·ÔÝú~N‰Ýê.K­®ª_Uýª^D"‘ЀtoU蛀uˆssss²[aØåõòÁ»dölÝÞÞÞÊTDþ@ý†aÈf³Y‘©ÊŠòmvÜÌÌÌL¯ý¸S©TêãÇEΣiš6???¤=¹‘V«ÕÚÞÞÞ–y ùD~ÊΚu'%à•ï‚ø&`g¢[ëWR©Tê׿þõ¯;æõòÁ]²Sd;‰&º…„,¼‡úÍÙl6+;£¥W¯NÅb±(s¾ãããc?fdÖçš%é“I¨ªªzåÊ•+A™â«(ß:<ïß¿/»¿y©T*¹1Bv[Ñk×®]K¥R)§ËáGvÖ¬;)N§‹ÅbqX×ï—×¾ Ãæ›€¢(ÊêêêªÌ4DEùvÃZU 2R©TêíÛ·o{U&^/¼Gt—EQ”ÝÝÝÝ $à?Ôoð"§’ôÕjµÚääädÐêØõõõuÙO­V«¹ÕáÙÙÙÙQUU=žåD߈vþ5MÓ~ûÛßþVæ=VUUý§ú§2ÛQ@—Ïçó~ xí»0l¾ ìïïïȾîéÓ§Oˆx[eŽõzù ÏÎÔW¢» hš¦™íù n¢~ƒ×8•¤/hÉþt^ÜßÜÎl"7FžS©Têûï¿ÿÞÉsºE4až¤îüã''''C¡P(—Ë匎¯ÕjµP(šœœœüÛßþö·7nÜjÀ‹ß…aóU Õjµìt‚b±XìãÇíN#Í@ëõòAžlÖZYÉd2)²éÇ‚4-€¿P¿ÁKœJÒ´d:;‰âµ¿¹­ŒêtêÏ?ÿïÿþïÿ¬Fô2ÔW*•ŠQR¸î W«Õj‰TUUÿú׿þÕþ_4X^þ. “¯Š¢(ïÞ½{'¡ê‡Ã[[[[²b±X<===6âõòAN«Õj¹u~‘€Œþðê7 ›SIú‚˜ìOg'Qœªªj2™Lb „ì2]>ŸÏÛÍa¡wüýø<ùÛßþö·þçþg£g¯“ÛÓYôûäÏþóŸû½Ö xý»0L¾ Ø…ÐéÛJ™mÕ¹µ•ìãõòYI§ÓiÙ$E²ü’„H'­Mä×)ÇïÝ»wÏ긵µµ5§FÿËårÙíÏ·ŸmØ(_°Ë7h² ÒÎÏÏÏe“,ÙÙªÐn6åa¾~¯ßàoN%é b²?ÝýÍïß¿Pžf³ÙÜØØØ°óÚX,;<<<ˆ‰VOMMMÉŒŠF£Ñ¨Uf[MÓ´ÅÅÅÅ ¯A‚¿5›Í¦žlH†h²T*•dÏíDFp˜£~à 9™éŸçƒ7´Z­Öâââ¢ìÎ"£NüÏÿüÏÿ¸Õù×éA€F£Ñð[çÖ|h6›Íû÷ïß÷êÃÃë僜—/_¾4kð^¼xñâÕ«W¯ŠžïÖ­[·¬¦£99õœBý†AÉår92ýS½^¯¯­­­ »~ÓjµZÿöoÿöoƒh6›Íæììì,ÿàñm@Q¾=>>ÎgÀϨߌÉvÖE¶€žžžŽD"Ñs~øðá‰eáeõz½‰D"nÏ(•J%òB`”. (ß2dÎÎÎΆB¡SUUÕ™™™™P(ê·ñáõòAL¿•þ™±-€  ~ëM¶³¾ºººjU/ÈìsÍè?ü¤P(B¡P(—Ëåœ:§>Ø …BtþF£{{{{炲Ùl–ò¹Kæoî÷ïÇãñãããc7¯U­V«fç=>>>I™Õ÷DW,‹Ã.+œá¥úm÷_±X,Šþí{{{{f¹dEþý¬V«U§þ`dïy‘ï0ª~9ì ’>2¡ÿ?›ÍfËår¹û¸aM1ì.ü‡5üðs£^¿É¬ÿý›˜˜˜9Ÿªªê“'Ožˆ^ð¢Q†N äQ•J¥Ò+iÑ‹/^t'q%³þ¿Ñh4Þ½{÷Îê¸d2™ŒÅb1‘sZ%Œ–‘(Š¢Ÿ?7pxxx(º€(‘κ¦iÚêêêªè9E–0ú0ø@¿>}úôiØeþ"ÒY—ýW±%KKKKŒþŒ2K! IDATv €TUUwvvv†]à/Íf³9999éä9+ÿàä9£…&XC FceeeeØåÀ z(•J¥ÙÙÙYÑ-yðºÀ2™L&ô¥R©du¼¦iÚüüü|( m€_6ЩP(B"‘HD&/`¸ŠÅbñ\Ðñññq<»Ì ø7ãñxüøøøXôººl6›uâo>F£{{{{" ˆv»ÝN¥R©a—ÀhI¥R©v»ÝyNííííE£Ñè°ËŒ`Ëf³YÑÎwµZ­:uÝjµZ½.õ7Œˆ’ŠÅb±×ëEŸÉž0êFbœV¯×ëW®\¹¢ªªjul"‘H,///¢\Mñx<þôéÓ§"Ç–J¥R&“É8qÝb±XL§Ói;¯ ‡ÃágÏž=#8ƒC›šÍf3™L&E‚>|Èh'Üòüùóç±X,fu\©T* …‚×Ìf³Ù|>Ÿïç‰D"±¾¾¾îDyÖÐÑ @8¿zõêùà4ÑQx';ÿ©T*õâÅ‹VÇýùÏþ³Õw#N§¦u€@~#º~Õɵ׀èºg';Ø¢÷º¾ÖZ´Ngmöh#Œ0üHôÙÅh'œ Ûw‚È=nT/[% ¤>m`„àWýt"Ù÷îìXm3hµã…ÕëÙ6st€F€Ÿ‰<ÃØýÙòÏ鎎Õ5E—·X‡ïÆh"#Œ¿yŽ‘vˆttœîäX]SvY‹ÕùønŒ0 «iÚ4F!ê~t£N´ê˜Ù½‡Ý:/ü‰Šy…ÖOCϪÉè 0ÿò/ÿò/Ã.ü#Fõ«_ýj×üÕ¯~õ«aLÉÿíoûÛ^?—$B}&Ú±uªƒk•§AÇR \b¶^ÒnÂ$³QI*u€×‰vTÏÏý»ó†è¨x?íN²Á¿¾¯xžY‡]¶Ò7k4±¤à¢A¿Öm";=Øiô";³BÇ”z\`5 :jo•}™h>ÀODƒ~ÛbpЗ̌ ?¿¯²ÈMø„Q“©Íð³~·`rj '§ÙuðëhŒ1úÀ.ÑΫ_òˆlóx~î\ç[vê¿_ßW;>An‘©$ݸ߬¦Ú­€ÁÖh„@ðØ ó>à¢õŠ×gº‰v8zÞ9ñì=?n–@ÿdrYxýû #à-2_üA}ÈŠFâEôû`y/»¯!òšAU,///'‰„Ù1ªªªÉd2Ùl6›ý^/•J¥>|øÐì˜\.—ûÃþð‡;wîÜQUU5:.‹Åž?þ¼ß2Á¿:ƒét:Ýïù‰Dâôôô”Ù}Î"(‹Å­­­­p8vYºÕëõúÚÚÚšÙ1/^¼xñ›ßüæ7oÞ¼ycÖùWUU}òäÉçKùSÅb±èDeˆŠÅb±7oÞ¼aÖ0ZD‚^ì¬f³Ù¬U\Ó4íþýû÷èüÇãñø«W¯^™µsJ¥RI €H§Óé dÚ>2íV»9‹ÅYà @Åb±èõ‘ê••••F£Ñ0ú}8ÿéOú“ÕHÂÒÒÒ’ 3ñx<~ïÞ½{n^cM'#2n,‘H$nݺukØå0X"A€t:öJ‡"ÇŸ>}úÔìMÓ´Û·oß®×ëu'®ùüùóçV³ …B¡ógõz½~ûöíÛfA€§OŸ>¥N úò'gÊšÉçóy¯|gýŒÐE$ï­V«µ¸¸¸hV [ÔÔÿd2™4kdnº{÷îÝa—Àà‰>|øpØù¢ÑhÔj¶žÓÿl6›5›•W«Õj™L&Óëwõz½þøñãÇF¯õâì 8Oä¾uA€þ:ˆDà½Dd)€‘AMýW:`®¹¹¹9F£€ÑdðB>«uÿNwþ­Ú:F£ñàÁƒfç¨T*•R©T2ú½—fW x:s]A Ã£Gùm¤Új)€‘ALýW”o ¹¹¹9ÙוJ¥RÈD$‰8Õ‚7´Z­Öììì¬Ùç>???/;ë%‹Å’ÉdÒ­rð6« À0G¬­f:ÝùWó©ÿªªªwîܹÓjµZVç) ³ €fW ¸ÈñcO Û˜œžžžöЦêÙ$íné€àòë:u;K:“ú¸mzzz:‰DDo4ññññîu‡€¢|›õ‰D"fÎ^¾ûî»ïÜ*ï³  cÄÚj$ÞοÙÔ;  …BÁè} ‡ÃágÏž=ó{»Ùl6''''͂ӡP(dÔnÑë-«×ª]äø±Ï×€î„Xv×m÷ TkôÈvTå[¤|fffÆìnµþÐ 2KFceeeÅí2馦¦¦D3Âjš¦-...ŠŒ<`´ÉÎ|¹téÒ%7ËÀû>>-‡.ˆkàE8^ÜÖ†Ï1¸:×{%>Õ“–žŸ;³6êGp—Èzr«çëÞÞÞÞ… .8™°òèèèHfkkò~áDR ýVYù;©¢ÉR©TêÚµk×úø=£û½6Ú‡µ—t:–ùBšélXÚ ®”Ëå²ìCÂëvvvvTUUEŽM$‰÷ïß¿f€Ï1˜º©²KŸÂápxkkkK=‘à’I°i'04Ȥ[ÔåÕûžrɕ˫¼ú~9•\·óú"í‹Îçk÷@n«ÕjýÇüÇ8½³”ÌöѰ§ïÕjµºµµµ%ûAåóù¼H&E™­Ù¼J˜ØyŸŒt~!Eø ™†•2ùµ¡Ól6›»»»»¢Ç'‰ÄçÏŸ?2û:Ÿcpé£NooªwF¼ÞèÂh¢~÷émŒ~žµ±X,öñãÇnÏ2”YÓ¯išvtttÔÏõÜÚÀËlô7«ŸÊÒªF£×¯_¿.sNUUÕ™™™£=%Fc|||¼{J7öf×£lù|>ïô¹;•Ëå²Yã>›ÍfîTÕêêêªÌ´#}VË :W|ŽÁ¤?KíŽ6ŠÊçóy‚ð êGŒl6›u»áÙ¾ßÁÁÁÁþþþ¾›e "Û€õõõu'*ÌX,{óæÍ›^Ñ”«W¯^½xñâEÑsår¹Üäääd³ÙlÊ–£P(ºƒµZ­&{]±X,:9¢ÑO9Ø=A\½^¯?~üø±ìëòù|Þ͵×|ŽÁ”J¥R?üðÃt>0J¨`0ü¶‹Úòòò²L›huuuÕé%£ÀV ›Ífœ&—H$·nݺÕýs™5 ¥R©T©T*N•IQ%“ÉdìªÕjÕíQ Åb±è…røM¥R©Ø™¢¯½¶›ìÒŸc0e³Ù¬:AÀ Q?À`¤R©Ô‹/^ »¢R©TêáÇEo4wïÞ½s³LA%XXpº OŸ>}j·Ó¤iš¶¹¹¹ét™ì¨V«U·ÖÊýá§ …BÁî²}Y€>Ç`ò[Dpõ# Æøøøø³gÏžùe!FeËËè¿}Ò€K—.]’™–/*‹Å’Éd²óg2I ¼ X,½Ð¸±ó%ÂÏ …Âüüü¼LN€Nz ÀnÒ'>Ç`ò[Dpõ# Îüüü¼_–F£Ñèû÷ïß˔יߣD:ð¯ÿú¯ÿêVÅy÷îÝ»nœw¼4¢ ›;?§o·âÄm=û³l6>Çà‰ÇãñW¯^½¢óQBýƒuóæÍ›Ã.ƒ;ÿF£ÑXYYYqª ccccNO×jµZ³³³³Ý9æz) ‘sV*•ŠÈù¬–°÷½  “ææææ:§Lúôé“ÈëÂápx˜7º×F‚°uâ°èÙݘž-›(0(Ÿã¥K—. » ^ñèÑ£G~ÈÂDccccÃ.Ǩ¡~ôb·óãÆLýïO߀^ÛêÍÌĮ̀ªªÊž+‰D¦§§§í”ãÞ½{÷¹{§[·nÝêwšM©T*™ErDßS;['êjµZM$ªÔÏgìeñx<þùóçÏnN™ÒZ- às ž~FA­¶7Õ ¹³¥)`õ#xÈ3-—Ëåܺ¾Î¿ªªê;wî ³óttt„àC_€R©Tší~#šÍfsrrrRv½î‘ü£££#Ñõ×±X,¶³³³3è @4ÚMŠØÙh·šú¡¿§¡P(dÔ P{ÓXôÆU&“Ɉ¾¦³<º~ÖË[4¾yóæÍ FgÓétÚ,I`P>Çh4ššš9VÓ4íèèèȉëzÑ‚ìÈ£`•Ù¤sKÓ~¶2 š~̰‡ú1õ#€`è|®Š<Ó:§›; °ÛùO&“I;[½[I&“IÑöÿ—/_¾8}ýa°¨Õj5«JùÉ“'Od£àÓ…÷÷÷÷D_ëdöuQvG7jµZÍ΃Šòãš’^ ééééH$=—“ëhêõz=‰DdJ^±¾¾¾.ú9vŽFõ3Ú‹Åb?~üØkI@P>G·ÖUùM*•J]»víšÌkŒ¬22™L&È€V«Õ …Ãá°h0 Π~ü‘ŸëGþ—ËårvŸ«Šòc0 ßY†v§ý_¾|ù²E‘K:/º<ÝëlTUUŸÚj·ƒ‡Ãoß¾}+šÀˆW?G™¨j»Ýnýúõ«ÛeÙuÇ"V™L&ä¿Íúõ# —¦iÚüüü¼Só¿ÿþûïëõzÝÎk½¸æ?ÇïÝ»wOäXUUÕ7Ê1h¶¢Q˜ÍÍÍM™Êwjjj*Fõÿ¯®®®:ÙhM$‰ÓÓÓÓóóóó~g ÄãñøÜÜÜœÌkœÎ\©(ßF¾ÿþûïí¼öúõë×;ßïQ$Ú93Ûr$“Édì®û ‡ÃágÏž=ëçsðâç(;ý÷ääääìììÌÍ2 ƒìºãF£ÑxðàÁ§ËÑÏsÂËd¦ã]»víZ¿Á6ˆ¡~€á{üøñc»v§É̶U”Á$ü{þüùsѪÝÝÝ]·f! št@Ó4msssSôø¯_¿~m·ÛmÙëèêõzýÇì¾ÞŒ¾dÀn0@ftSQ¾½w‹‹‹‹nÞȲïw"‘H¼ÿþý¨6rD;gªªª/_¾|ivŒ¾îÓÎô¨D"‘¸uëÖ-ýÿAø————eôAI¬ÒMvÛ±ÕÕÕÕ ¾n‘ 2;lƒêG®F£Ñx÷îÝ»a—CQ¾m¯N§Ó¢Ç×jµZ¿Ë ­‹Å¢h™4MÓVWWWÝ*Ë I†1M×N.YvòȬQEùðá÷£pgggg''''2¯ÑgEìíííZCGtºLÔ¯P(ìL;]XXXÐß¿ŽÕjµšÏçó2¯yýúõk·Ê3LSSSS¢Óÿ½TYû…NÝçÏŸ?kטQAýÃå•Ù¡nÍ„ìT,‹2íÔµµµ5¯Ì¤p‚t@vš® ·[³Ùl.---õsQz Àj›6E‘ßß|»yå§Ë#dö«÷3ѤP²Y?ëõzýÂ… Œ²Q÷rñâÅ‹W¯^½ª(þüãñxüøøøøüüü\&Ê«(ÁZWÕMf4kŽåÙÉ5Ó9û+›ÍfÝ*Û(£~€áñR»J&!ì ¶ú“íü»±p{¤ìòTMÓ´û÷ïßâ•t@v*´Óô½tµ¯u"‘H¬¯¯¯wÿ\v?áA~[­VËíDútH¿|ˆ®ŽÅb±G=²s »ÛaýsTUU™™™qr»;¯‘íÁ>= ç ê£~ FýÀ¿†Ý_ÓÉ$„usdµZ­Ê,OÕ4M»}ûöí ­ûïä«2™Lf|||ÜéH~/ét:ÝïTÐA+•JÅN6zYúÈG¿[*K³ÙlîîîÏçóÅb±hç:v§vñs,•J¥P(šœœœ bTÕ.¯L×ó»L&“ …B¡AÎC¨n-ƒ¤óÿs¾ (Ê·Hþìììl( ¹àéÓ§OýV … FÃô¤Z~\3»ººº*ÚQÈçóy‘‘ÝD3b÷zøís´“OÁ¯dGB FО«ÐÉ+9…DÛ¿š¦iv“´š‘íü«ªª^¹råJ;ÿŠâó@§J¥R ýƒ3b±X,™L&<ç d2™Ì “'–Ëå²ÝQòa©×ëõ>|=>N§e„E£Ñèõëׯ[göð Ò稿ŒŠÁ Åb±x~~~¾µµµ%ºý"FSž«€Ÿ*‹EÙÎ2™LŽÂìÔÀ:Éf_Õ¹O»ŸèÁ‘Aväóù¼ßF:}úTôø gû72rž1Z¦²ïÞ§]†—Ö= …‚¾\­ÆN?Yó‡¥Ùl6ïß¿ßNâ0=ësg @¦óßh4++++2ׯç¨oB=ô³ôƯ³kœ411116666ìrøÑòòò²È,›^r¹\®×ý …BlO9Ô§<þü¹Ì€ÀãÇ}Í·‘ èdéÉ,dGö¦¦¦¦¼ØÁélì8Lñúõë×½ø7›©×ëõ+W®\±»Ÿ¸8???íükš¦-...ö³õ‰>ÇΤœ² çD"‘¸uëÖ-û¥ô¦³³³³““““a—#ÈDsltÒ· ¤“ïêGk~¬ÀëR©TêÚµk×D¯ÕjµQlŒ|@Qä²Àë#² {¿Œîu&SìwįK&šÍfóòåË—±Å¤[[ ûs, ÙkÞ½{÷®\ɼ¯ÕjµD³Úú5Ñè°]½zõêÅ‹/Šßh4—/_¾¿©Åø½~ *¯~.”+Fíýê^Je$‰Dú­ŸÇÆÆÆ&&&&D?:::Õ}iØ+Š¢\»víZ*•J¹UžAY.¡(ÞJîd—Þ¸sbýg­V«…B¡P¡P(8Q6»õ9îìììˆO"‘HdzzzÚÎu¼Læ=d.·‚ñx<>777çä9òúõë×Ã.ðȎ8ÛɭЉúÑZêG¯ô}/Šrï—·LOOOG"‘ˆèñ234E}ÿý÷ßû¡.ót •J¥þþ÷¿ÿÝíír’ÉdR4[dçÍ"Ó°W”oŠ………;eôŠQÞÖ̉ý¢ÓétúøøøxØ[@ ês$ žü{0ȉDâóçÏŸ¼ež§Š¢(ív»ýõëׯv®F£¢¯~gÌÈ“ln…nÔðŠAÞ÷2(W0ð~ù—“AØx<?>>>Îçóy§Îé&OåÇ}ÖÏÏÏÏ‹ÅbÑéóG£Ñ¨ÝFG³Ùlîîîîʼ&N§þ;R©Têûï¿ÿÞÉsÂX&“Éô³å],‹íììì ;02Ið‚º&Vv:t"‘H,///;]£ç„þŒÍf³Ù~¯açyzrrrrvvvÖïµ­ôhð*ÙQv™%&2¹z¡~„[¼zßS®`àýNåž*‹ÅÃÃÃC™Áaó| S>ŸÏŸÿƒUEQ”õõõuÑý£5MÓ6777;fgJi>ŸÏ;ÑȉF£Ñ½½½½­­­­ÎJ6›Íº5ÊüèÑ£G~ºÁÝ¢' ´› ‹ÅÞ¼yóÆìÁÃç/_¾|)sŸäóù¼SÏ8EQ”jµZí~Nt+—Ëå~ï·åååeÑç©n”×à šhæùT*•zøðáÃ~¯Gý/ô}/Šrï—wÈÎÂêwÙe*•JµÛí¶_Fý;ù*Щ\.—Ï;È6–³ÙlöüüüGÿh6›Í¥¥¥%;¯Õ—?jvŽh:‘¶ºÇdfQuÛÝÝÝôV˜Abg½®WãÆ4FêÇÞx®ÚçÕûžrï—¿ÙU‡Ã[[[[FÏúr¹\vº¬ÃD@ÙþÑ­V«µ¸¸¸(3ehìïïﻃ–Éd2¥R©$z¼×÷¿ÕÏÑ •J¥ÒÏ.A3ÊÛò9Åëï!õcoG÷4›Íf2™LŽjµ¶¶¶&ó\…¹ÕÕÕU§G”>|øðá¿ÿû¿ÿÛÉsR?ò\u’Wï{Ê ¼_þÕl6›÷ïß¿ÏL cžÔëõú•+W® £‘¬ªªzåÊ•+v©²kÀƒæñãÇG½qF£×¯_¿>ìrôƒÏÑ]Íf³yùòåË£¶ T*• …BaØå§§Ø‹,{³‹ú‘çªS¼zßS®`àýò·z½^üøñãA_×/ÏxOåÇ}ÖC¡PhP†Z­V›œœœì';u¡P(ÌÏÏÏRôIÓ4m~~~¾R©T†]»ªÕjÕ‰-<Ö×××ö îæµýɃð9ú…¾sÄ ;DÃê„år¹w85Å^fÙ›]ÔpŠWï{Ê ¼_þV©T*ƒªkJ¥R) …üòŒ÷| S¡P(„þÁÆk­V«…B¡P&“É8q¾z½^D"·ÚÝ#j•J¥2>>>>È‘ÅZ­V‹D"?D½¬ôÚ D4( ï*³5ÚÑÑÑQ¯é |Ž£C¶¹9=ºV«Õ:Ÿú5Q96Æøøø¸_*F¿ê7°ÓÏÌ7YÔpŠWï{Ê ¼_þ¦×5nµ¯ôŽ¿ß7|èÔ …B¡\.—³sýƒs²ãßM/«Ý2ö¢7¨n:}d±ß÷ÇŠþþ¹õÞy…Õþ ýìúåË—/F¿ãs-™L&ãt€Óê³Õ+G7‚zPuvvvÖ5ÏccccNŸ×Ï쎮;1óÍêG8Á«÷=å Þ/ÿÓÛWNTUUgfffüØñ¼x<?>>>îî ííííya˵h4ÝÛÛÛ³êPºYîT*•j·ÛmÑ2œŸŸŸW«Õª“eðšjµZ•y?úu|||Çãý”ÙëŸc±X,Š–«X,U.¿°óùf³Ù¬×öÂsÊŒÑsÞ eó ³gšËÜà…ûÎëÏU˜óê}O¹‚÷+ôY»VøL}Ĩ1ª@FêQhÜ :0 ^™À(Ücý0z®9Hò#™NÏ~ÿ2úœGáù €Ó|»îi6›Í¥¥¥¥îŸ§Óé´S#‹ø6…èåË—/‡]·}úôé“è±SSSStÒ êæÍ›7ÃápXäX£\ð>£lÜù|>O€è©R©Tz­“yñâÅ ¦ÁôŒ°½]¼xñâÕ«W¯»ð¾ lµ q++++½÷=}úôé(Î~À.0´ºººÚð$‡†U¦ µŒ°GGGG¢‰sÂápøÙ³gϘ+ËËËË¢[m*Šy²Mx_«Õj-...v?Kb±XìÑ£G†U.ü† íïïïtÿüÚµkט`Ï(f„ýúõë×v»Ý=>‘H$NOOOYß‹^ôõàù|>/ó:™¥(ð&£gɽ{÷î1 1 -‡oÞ¼ysØåpC&“ɸ±G´¾Ú(nÕl6›»»»»²¯Ëçóy³¤nde ‘ìï[[[[¢ëþuªªª;;;;n•ËÅbÉd29ìrà`èÖ­[·Œ¦Ø^ºtéÒ Ë3(Ý{D‡B¡ €¾ô¨vü;½~ýúõ°Ë€Ñµ»»»;J³n‚*™L&c±X¬×ïîÞ½{wÐåÀŒ}ÏK«-±ªÕjµ\.—~?jÙÚ{¬ …°Ëí;;;;ªªªÃ.F(ïÒ· 5ÛTŸbV'1¿v0ú:ëa—£¡Ùl67666d×mýj4wïÞ½v9`.N§ÏÏÏχ]‚ް…=µ!«P(zm- ¸EUUõÎ;wxV»< †laJ-ìÈd2™R©Tv9|F£qùòåˬý>MÓ´ÍÍÍÍa—?lÀhÍöììì,£AýaJ-úQ( ¡P(Äl¸AUUufff†gýè8888Øßßßv9ðƒÀàMÓ´ÅÅÅEÖèW&“Éô³ËÂ(2 lNNNNŽòHw.—Ëñ^Œ&–y˜Ðw°Ãjçdè»P'¸,•J¥ÚívÛª‘•Íf³Ã.+ ØDÔÔI0ÕjµzÞC»Ýn§R©Ô°ËlŠÇãñãããã^ÿ^ŠÅbqØe²ÙlV´ãßiooo/F‡]~`¡X,ítþuÇÇÇÇñx<>ì¿°;òÏL|BvÍ¿r``Œ²V÷”U£Næ™És8åVX5RŒ¶%J¥R©v»Ý¶j´d³Ù¬Õñýl}F£{{{{FׯV«U™ó‰®×dJ& —x<Ÿ›››3;¦T*•BffffTUUŽÅb±G=²SÑúº»Þ#Ò(0jˆŒnt¿Öl=¤ÝÑ ³rÈvÒeI£4%“ Ƭ®³ x;Y§)ŠÜRžÝœU§Îhô\$±‘ÑkÍ®)Ûø0­—U`5“ÀȨŒ”ø1`ô™ö3{è1muN£à’LÉèÚ²³\¸Ë¬n²ª3Ì:ìvž­¢Ïn¯<·€K¬F»"e³ÎÕh„hçÌ*!;:ow«¦Qi4ù1 (½ËÝÏ’£ûΨ.XéÄÑQT£çŽì’)ø›ÑóRôÙhTÉ>»Dë5/=³`TÈ >öÓvÅè1̰°°°‡ÃF¿_ZZZj6›ÍîŸßºuëV"‘H½NÓ4mqqq±Õjµzý¾Ùl6—–––Œ^ŸH$ëëëëF¿W”o°r¹\6ú}­V« …‚Ù9:¥R©ÔÇŠß)‹Åž?þÜÎk1áp8üìÙ³gv¦Ò>}úô©ÌkÖ×××;/ºt:¶z¸OOOOG"‘ˆÌõ8Ÿ‘߈Uçääääìììls6›Íæóù¼ÕqªªªÉd2Ù«¾—%pè òË/ËîÌ>3zOvÉ €î³ó, ÈŽ }ò †ž€T*•ºvíÚ5£ÕjµZ¥R©tÿ\¤´¶¶¶V¯×ëfÇT*•J­V«ý>N§ÍFÒ^¼xñÂ赪ªªOž ý˜ƒebbbblllÌíëˆ+ìü‹Ò»VVVVFÃì5‰D"±¼¼¼Üoùiv×á[5Ìd:¦"“îžÈkdö"Íßýîw¿³ŠšÉL¡trTbPÁ¿.¹gúY²bô÷:1âØy/›-y=ΙûË©k÷3ÍMvë5ÑÀKÜÑÏ÷ĉüNÓ?¿aæ­_œ|N÷³ÔIä¹a÷™À tcÌÈ<3:Ûƒx]çsÐÎŽeÌ-?›`5úo4õß*º/;òÞl6›÷ïß¿¯išftL>ŸÏë7¢Õ¬¢Ï\0"2b‘ËårøÃþpçÎ;V[4‰,Èf³ÙÓÓÓS‘iá"ÒétúüÜŸ£aƒÐl6›»»»»fÇ$‰Äéééi¯áááá¡Ù=§(в»»»ÛýI&“I«×Y)—Ëe½FK^TUUwvvvú¹Ž‘ûKoP™-Ë‘¡Ï„é èq™ŠòãgO…7Xz§OäûåGŸ>}údô»/^¼0ëÌ>þü¹Ñ{"²„`yyyÙª~qrä_ôšš¦i««««Ý?÷îÝ;«Yv—m0Š~0[ûoÖ¶›3ÀL½^¯¯­­­™óâÅ‹¿ùÍo~óæÍ›7f EÙD<¿zõê•ÙßT*•Júû!°pb)€]ù|>O”º·ÕÕÕU³Ï­FÚ»wïÞuãzݵ¦x÷W,‹Z}§ŠÅbqkkk«Ÿe<år¹L NÙÙÙÙ1 ë.£nfA¬ííím£ü:Š"¶î_Ó4íþýû÷êüÇãñø½{÷îY÷áǽ–¶Z­V¯gh7– æ'€l6›5j\˜u Í^§(?í(˲Z‡ÃúÓŸþd5º €0eQ”Þ‰ëõzýöíÛ·Í:“OŸ>}:¬éïf¹F™H É®^9/¢Ñhtjjjª×ñµZ­ú‡™™™³Y%"VWWWÍ:NÖýe6b*šèLD>ŸÏóýDfåóù¼Ìl#UUÕ—/_¾4ú½ÈºMÓ´Û·oß¶ÊÓ#ê.Õ¯kÖÉ™ (ŠrïÞ½{^Y^AÆ’ûÿ€h4]XXX0:pccc£WÚêuF£±²²²b·€­V«µ¸¸¸ØÏ­ìÔ«€F­V«e2™L¯ßÕëõúãǽvØ»<|øð!Û„ü\¡P(˜%ž´Ãh·‰±±±±‰‰‰‰îŸwÙšÍfsrrr2—Ëåì\¿Ñh4Þ½{÷ÎÎkíFÜhú¯UBP;:—ýxòäÉ“~|ŒêhEùVO[Í’s£óoU—êŒFÿu¢³†]¿àÿ?`¶}ŸY'ÞlmŸªªê;wîô;ÙÏ­©ÿf£$F£ñàÁƒfç¨T*•R©T2úý0GâÃápØ,`3Ê2™LÆìs“Q*•JFA"£íúzå P”o÷S( É(¬¶ÛT”o÷òøøøxHÈõc±X,™L&EË©ªª:333ctMÑàÇÅ‹/^½zõªþ‘Ý;4MÓæççç;¯7>>>n5Ò8ÌY<ø–w% …œú®‹Õ–·2¬íVkðÝèü[ t^[¤s/: àÚµk×r`ìŠb^Q›u$¬ÖöÙY÷oDd; 'Ê`6]Q& Q( f TÙ‘x«Ž’L'’±B¡PéÑ;Õ½Fþ­m×§Ëd2™ùùùy«Ù0fy=˜ …B³³³³2Á¹L&“Y–ðÝwß}göûÎe““““fßO½¼V½p8¾yóæMýÿfMEù ‰D"‘î÷¨ÕjµfgggÍÞçX,{ôèÑ#³òÀ½ž{v—“™ÑgÛˆÃìê.»U°X„U½dµ ÆÎ¿¢XuûûûûVljÎ rëßw«ÏMä¹ÛR©T²sÿ`›Ù6;f#ÕfÛL¸‘Lv[ÙQv³÷Áî6CfÛŽõÚb¬³ ý¼‡VÛ±–YŒÈ='»]–Ñ9e¦–mU4ˆÏÕj[®^ÉËþþ÷¿ÿ½ß “Õ=­_¬Ö¥‰¾Gf§—¶²UVÛùégw‹L«m*­¶4ëgKM32[W9ñÜë÷¼VdÚv¶ôÓ½tl3ƒÞЮAlHŸ3{ؙ݄f 'n^#vnjf•u?$«/HwÅ‘Íf³N4d¯ ˆ²ê¬¸õ°·º®~O›}—eŸ ýì×w) (ò«¿oXEhØ ¤¹Õ0C`t€‚âfSͦwN%–y]¿ …BAdªœlêõz=‰Dz«×TaQVS »×‰W*•Šéã½®{tttdôû©©©)öL†,«ªªª;;;;ƒ,“îõëׯåÛ½m´ö_vW„ÍÍÍM£©¿ƒÚÊ£¡³.6ZÊÕ™·Âªž°ZÖÐO½fFtí¿¢˜'.4böìÔa”£Çˆh ð’_X¿2[S>11116666ÈòÀ¿ôH¯ÕVdvô¢Ì:Ü;å °œØßßß?888èõ;‚hpK&“É ²Óî$ѵÿš¦i››››²ç7ûNv"á-ì0 ÷bµƒx±ZÝÍ,ñS$‰LOOO²üð.«éª§§§§VúR©TrböJ/VÛ‰uŽì_ºtéR¯cNNNNÎÎÎÎd®k5“ÀdFÿE“ÿuM¨($¼…<™™]ŒþC”hÂQ;3˜;xX÷Z4‘ý”Aëgç©T*õâÅ‹F¿¯Õj57³cͤa ðSW¯^½zñâÅ‹"ÇnoooÛmäîìììXíF¢(?ß0Çãssss¢Ç3úÀ¯xètk`X:·gs3ZJ¥Roß¾}k4%³Ñh4?w~ëÏl6›ÝÚÚÚ2ëü'“Éd¯„ƒŸ>}úÔë5v:fSšŽŽŽX§|“L&“¢uW?Óÿu¢Ë…; Æh¶W7FÿøY^^^fäÿ§ôćfAEQ”|>Ÿ??wwokK$‰~øá'Þûb±X,—Ëe£ß›uþå[ÇÜh›°………™@…Ùˆè$0 D;Ùv³ÿw;;;;;9999–d€°"3ƒ…Ñ~GÀ#âñxüÞ½{÷DÏår9«L¢¥R©äf™Ý¤Ï@M|‡Ã[[[[NH]½^¯G"‘ˆÑ½4>>>Þh4fç‡ÃáW¯^½êg½mµZ­ší^Ñh4—/_¾l¶Õ Ù6a‰D"±¼¼¼,R–l6›5*‹- ’é<ÙÍþß­Õjµ¶···EŽ ‡Ãa–òÀŒèú¿þ[ͦìÅK+~/?àÒn¶ÔC_òbw¹K¥R©8£V«Õ‚¼üe¶SF7§Öÿw2Úö³‘™vAg•dUQ¾Í¥<:"ëÿý$|"‰D¦§§§EŽM¥R©‡>t»LN‰Çãñ§OŸ>uó‰D"qëÖ­[n^bD¦ü7Æ… .ô»Ö²P(œ ÔjµšìL 覦¦¦D÷NwcëL«`d§ëׯ_•Nm/2ùu‰DâóçÏŸGaöœÈúFÿÑ/‘ÙRSSSSN<£ …BÁ*Qx( É,­D°ð‰p8~öìÙ3«ƒÕþé^4¨Ù ²[À¡·h4]XXX°óZ‘)ÿ²ëý­ …B.—Ëõs;Ë€Q ³þ_f´^”Ù¶ŸÝ&&&&ÆÆÆÆœ.ƒ×Ùͯ‹Åb?~üä<:Ñh4zýúõëfÇ0ú hx„HÃÈ,"¯ob5µO”hôPW«Õjv®#Rù:eÔg‹Åb¯Äw²Dr5tʉLùW÷:Ú•J¥"²•a·Z­V ú®€]2ëÿEn´^”L"@™™tAÇãŸ?þl7¿NГê;ܘµo&'''ýÿÆïÛð ³üccccfÇŒjƒGÀ#D·3ꕸìüüüÜo£þ:™õ£Nõ­ zÝ?¢ »jµZ=???·EêNX)2å_UUufffÆÍŽvgCÏlF@çv›ŒúÆD´:§êÎÎÎÎNNNNDŽ•IP©T*õñãÇN̰Åä€À bÃAÀ#ö÷÷÷†]ŽAÝ{×)N­¯ ’r¹\‰‚‹Ní\+Çã¯^½zeõ¶dT«Õªèß\©T*F£=‘H$Â>ÏÃeµUÒùùù¹Õl’|>Ÿ·:šþˆ$OÓµÛíöׯ_¿:]†V«Õ’ ,ŒJ"@7–æóù¼ÌsÀdž—€ÛxD«Õj-...Š®eñ¿ÿû¿ÿëÔ¹Ü2è-O˜^å.ÖJ4™Ü£°ÍVµZ­:µ°[:NÒ@'Ù€c'‘„©£6K ÃCÀCêõz}mmm͉sår¹Üý×ý—ç ¦W¹GUU5™L&Y+ Œ™NNNNÎÎÎÎÜ.Ó(“MöW*•Jú2§J¥RM–:J;v¹±ë àÓoÆò~÷Nì¨Õj5%£Å+Óée’ u˜l²¿\.—+ …ΟU*•ÊÌÌÌŒªªªÕëõe[AM±»›‰èóÒ+ÏU’©„;Õjµë—a¤P(ìdÂ7Óh4ãããã$ˬx<?>>>î'g‚½q?èë!x¼2Äe`2Éþ¬®6›ÍæåË—/‹Ö$z³(…eJ>Õl6›““““¡P(T*•JfÇær¹\¯Œåf[ùj À­DT~ÑkË#«û«[©T*é¯õB£ÀàÑP>™dF£qùòåËV3µôzB´n 9 F‰ÝíLy^ÂK~9ìÀZá†]7|ùòå‹èzE'°õçܼ¿ô@–çFpù™çiš¦ùq+Ü~‹Å¢ÕnºZ­V“©U( ››››oß¾}kõÞêÉoܸqƒ °wÕëõºŸ3Ñ{¡ü¢Ï»}™ß#¸‰ªÍÍÍM'w>°²½½½ÍCËîtXôV­V«¢ÿ\.—³»L«^¯×¯\¹rEdI"Éo4MÓ:w ˆF£Ñ e÷ï5£´r’y ÕþþþþÁÁÁÁ ®¥iš¶¹¹¹9ˆkà†T*•ºvíÚ5«ãœJ ¬ÏäªÕj5«cc±XìÑ£Gú¹àe_¿~ýÚn·ÛÃ.ЪV«ÕÚÞÞÞĵö÷÷÷q-†¥Ñh4.\¸pÁÉ\?™L&#›3E£žo ÞGÀC²ÙlÖí,Û^̸ýòåË—²;رºººÊô<` N­V«¹• µP(æççç¹tð’³³³³““““a—è ]³Ùl.---¹yZ­VcÏÔÔÔÔ¨%4£'ä‹F£Q§Ï],‹¢»AÔjµZëû{!á4¼Ž<¡R©TDÖÚ¡ªªúäÉ“'nœF•ûJÃ<‘H$~øá‡R©TÊ©sÊ$xxƃ4†“çTUUM&“I«}îÑ·¶v9‚ ^¯×?|øðÁê¸p8ÞÚÚÚêw¹_<‹lÙ«ªªúòåË—ý\Þ¥oÃg•ù½S$‰8™‹¢N•ß*øytttÄ’Sxx†¾¥ˆS3èü€{ìîsí´ï¾ûî;ÑcƒÒ0—IÈW.—ËÕjµjç:©T*õñãDZX,ful£Ñh\¾|ù2u.xxŽ™†kµZmrrr’†¸ãèèèH4ÜÄÄÄÄØØØ˜åÕƒ…B¡Ëår"ÇÚÉ ³ÞßÍă€× …‚Ù¬L&“v3àIúÃUv6€ªªêÌÌÌ _™¾/·Ì4F;ôÄ™ƒ¾‚'‰D¦§§§‡]?æ-0S©T*¢Yù‰DâóçÏŸãñxÜì¸h4ÝÛÛÛ]ï_*•JÔ¹à<¤R©TÜn`û­±Éd2¡P(dÕÀÉår¹P(bÔãëׯ_Ûív{˜eˆF£Ñ©©©)Ñã½²lÁIõz½~åÊ•+"ÛéÆb±Øááá¡Q^€x<ÿüùóçD"‘°:—¦iÚüüü|¡P(Ø)7`8À¬·ø!˜A"³v8ËtÔEMLLLˆ«išfµ}—_5›ÍæåË—/‹&Ò-—Ëåb±XìüY6›ÍЬ÷WUU½råʯ$w0XúL¡s ý&!…;i­V«µ½½½-z¼L²>QÓÓÓÓ‘H$"rl»Ýnýúõ«Óeð =‘®h|>Ÿ×“f³Ùl¹\.‹¼ŽdÖD;GŒr48y®T*•j·Ûm™sµÛí¶SÛIö{ýa— À™)õn$뛚ššIR§(в»»»; V™ä€²HöþGØ"³ÀÜÜÜœU:Y2³ ‚–ÐŒLr@Q$û€` lÙßßß?8889Öé¢Ñhôúõë×Eb@32É­är¹Éþ [Z­VK4±žÓ‰eªªªîììì8um¿MØMÏôO¢]@.ƒQÀ Û^¿~ýZôØ»wïÞuêºÉdj/ IDAT2)’±^QFgý/zrÀZ­V“y™þ ˜ÛvvvvD§™OMMM95*&³þ_&HT™L&#ºC™þ ¸ÛšÍfswwwW䨋/^¼zõêÕ~¯)³þT§ÿ÷"²C@©T*‘é‚‹è‹è{8ß¼yóf¿×»zõêÕ‹/^9v”§ÿ÷b¶CÉþ ø€¾È,¸~ýúõ~—LMMM…Ãá°È±Lÿÿ¹z½^¿páÂ=9 Éþ`t}ô2Ñd‚Lÿ7ÖjµZÿùŸÿùŸŠ¢(ùË_þB²? @ßVWWW{M+ïÖï2€x<Ÿ›››9vcccƒéÿ4™-*uÛÛÛÛ½ò.8y.™™3ºƒƒƒƒýýý}™×ð6 oûûûû"Çö³ àÑ£GD¶ÿÓ4MÛÜÜÜ´s  ÓÓÓÓ‘H$"z¼ªªêË—/_º}.™3t««««$„‚…è[«Õj­®®®Škw€Löÿ>|`Z;†áæÍ›7eFÚ—–––Œfª8y®K—.]=¢|ÛòÝ»wïd^ÀûG¼{÷îžXÎŒÝe¢Ùÿ5MÓDƒ€Ód:ÚVl§Î%³tFÇè? §h4ÝÛÛÛ;t|||ÇãÃ.7ÀyÙl6ëV]P,‹"ç®V«U§þžT*•j·Ûm‘ë‹Å¢Ý׌£÷hoooOtyˆçpZ<‹|öççççÙl6;ˆsÉÜ“ççÎ~‡à}¢myý“=Þ ÜÉÉÉÉÙÙÙÙ°Ëpžè,€X,K&“IÑóÆãñø½{÷îYÇè¿?Ôëõú•+W®to™H$ëëëëV¯ÇãñW¯^½êž_«Õj³³³³Ã¹N&“I‘Šb=úïä¹dò‚úf'C- ˜dr,,,,ˆŽÔŠ&ÿcí¿4›ÍæÒÒÒR÷ÏÓétÚjäðùóçÏ»ïUUÕ'OžŸÌ9eØY *ÈSi«Õjµûï5û,ÚÃ~dî)«e Ã:—ÌwÁÁ€Ñ ôíîÝ»weŽÿòå˷ʾV«ÕZ\\\Ô4M3;.‡¬ÎwëÖ­[‰D"au#—þ´ºººÚ}¯Ýñx<þôéÓ§Ý?÷BÆz™‘V#öÃ:ß! ø /©T*uíÚµk2¯ùôéÓ'—Šðˆz½^ÿðáëã®]»vÍlÄ1FE‚^˜þ {ö÷÷÷ºÞëÞ0Z ²½½½=ìŒõ¢köE‚Nžkzzz:‰D¬ÎÅÚ`4€mF xÌhš¦¹Y.€7Ì2™]g/²½ž“çMÈè?0À–jµZ=<<<ÍN«k·Ûí¯_¿~u«\ïh6›Í «ãŒfˆŽþ7ÆÊÊÊŠÝrb¸Ì‚<—.]º¤ÿÛl*û½{÷î s‹aÑàD­V«U*•ʠΥ(bK5ýFH'ñ;???O§Ói;×ÚÝÝÝe„FÇÊÊÊŠÕ¶€F³DFÿ5MÓ‡=ý?¥·-¬ÔU«Õj¹\.ý~jjjJ½ÙTöX,;<<<4J:¦'-;>>>v#PpõêÕ«/^¼hvŒh'ÛÉsE£ÑèÔÔÔ”ÕqŒþ£ƒêõëׯ‡]Ààˆ&ìž :ú¿¶¶¶FÇÅ»‰DâôôôÔé#år¹Üë:§§§§"KIì™f/ÚÉvò\" ýF  Œ2ô¯^¯××ÖÖÖÌŽ ‡ÃágÏž=ÓG{————­:lLý¾£££#}vÇׯ_¿¶Ûíö°ËÔ‹Õ4{™N¶“çIH FŒ%²ØW€ÑáTÛ¢{JµZ­ös>·–~¦/‘ý>Êoa‚è2ÑkæàêêêªÕr€1p]­V« …°ËüÁ(¹c½^¯?~üøñ°Ê~÷ËaÁ–Ëår"[Ô(Ê·‘ÿ7nÜ0ÚÙ¡R©TŽŽŽŽÞ¾}ûVd{ÎjµZ­ÙÙÙÙa—€Mnä¨V«Õaÿ]`øR©TªÝn·ÝX/ÌZd |F 3à˜R©Tbª?èT¯×ëV™èíb$À €ÑB@gü?ˆÄ@¬Ö1óIEND®B`‚CubicSDR-0.2.3/icon/000077500000000000000000000000001322677621400140545ustar00rootroot00000000000000CubicSDR-0.2.3/icon/CubicSDR.icns000066400000000000000000014444521322677621400163460ustar00rootroot00000000000000icnsI*TOC His32s8mkil32 Ál8mkit32‘xt8mk@ic08fÛic09ü”is32µ w'†ÿöÿý¯U!|ÿïüüµ'=/1*Nžd75GXW=½¿A]^bkW`J?=1%,¶º8?S…^3397CTV*¸µ4F6%6-;^lQJ_7!³­8\E5;.2T7 °®LM017I%P-ª§$H(F19!+aE4 ¬©!¢V&3'K9,2¯²^¦Q >\u<²ÿyO¡B07^],,hþÿ‰$?8#S6oÿÿ‰$),E*ÿÿˆ}ÿôÿþÿ“#+$!Šÿóÿþµ  %‡ÿöÿý¯0 }ÿïûü¶+A23*GSC;4K[Z?¾ÁIefkuZRKFB7+1!·»!;DZf=9=:ETV.¹¶5D5':1;^pOHr5$´­9XC:A-6zX#°®JJ04>s!d-#ª§$D*C4@(zJ7%«©‡J+9,M=!/ ®¯S…E!' .Od: °ÿ|H‚?4$OOmüÿˆ186%E)%oÿÿŠ($' 1(4F)ÿÿˆ ($#|ÿôÿþÿ”#2)!Šÿóÿþµ 3‡ÿöÿý¯2 }ÿïûü¶+A23*HWD:3HYY?¾ÁIdfkuZSJGF<.2 ·» ;DZf><:0:OY/¸¶2A4&:1>_…™¹ž)´®5R@?6^ÌËĩΣ ³®EF+;._ÄÁ¸¶Ú‚®¦3F'=81]áÖŽzeC«¦+ŒL/5F€QP{ê °¬![ƒH/5>Àûùÿ¢´ý…0H|T ÿùÿúë_lÿÿ†J6F#uÿöÝ‹Esÿÿ‹%=7,\Ó<-I,Œÿÿ‡=+9+‚ÿòÿþÿ”$#A3&‡ÿôÿþs8mk\$ŽW  ¾ÇÖÞôÿýðÜɰ¢tGÿûÿþÿýùûÿÿÿÿüÿC1ôøûüüþÿÿþüúûóõ;÷ÿÿÿÿÿþþÿÿÿÿÿù$ àÿüÿÿÿÿÿÿþÿýÿêËÿûÿþÿÿÿÿþÿüÿÕ´ÿùÿþÿÿÿÿþÿúÿÀÿ÷ÿþÿÿÿÿþÿøÿª‹ÿõÿýÿÿÿþýÿ÷ÿ”Fÿøÿÿþþÿÿþýñÿ}fÿùÿÿþÿøûÿÿÉ#`ÿùüøûÿýÀP Yûþÿû´C QÞ§7il32 Áÿ‚* $#$ (Xõ8%!% 1ïÿýÿ€þ€ÿm ,Áíÿý„ÿp €4éýùûùùøûÿÿnƒ &p·Zïÿþ„ÿm#$(+.042Nvx~kO;94-.kmhb]bEœÿÿ?YHE=::;ACHY—ÀzE;8899+))+$/mÿÿ>YYa`YMB4.2;Z^FBABBA@CC@=8A+„ÿÿ†,IFS\jwy—bplQSLJB<841,+1*2ÿÿƒ$04=DGQ\›À‰LH930(/212/<=,07!~ÿÿ'.-55;CFKQF1/174M7.3:?awdjC|ÿÿx/0CC07&2DG<4249@wg`mtn~pF4<wÿÿr4[9DAD!=<71-]uulV=5[O8uÿÿp'lRK9M+;;611H?& [G<rÿÿmAWbAM,8:7/5…+ c=<oÿÿm ?JAXO:0;5/3$†$g1Anÿÿn48N=1/2§8+698$mÿÿnG ˆA$C">0103D•…&!&!$mÿÿm:–¤›j (.1-)15 (,KF%lÿÿ]q'Yš“”_!$$(S–nw)pÿÿl#^“›g-%GWzrq#lÿÿÛ°.!G›†’X #=r}OJ& ™à€ÿ÷&'I†¤\QRngk)%%×ÿûÿüð$%G–]2s~O(&! Íÿûÿÿ÷ 7 LI80($"  ×ÿûÿÿõ6'2.!Ñÿûÿÿ7ö 5!65"ac_acbäÿýÿÿö*ðÿþ…ÿö$   çûöùøøþÿõ‚  íÿý…ÿøI+2/101/3??<4/0101->ðÿýÿþþ€ÿÿ‚* $ƒ#$ -K'!#"$ 1ïÿýÿ€þ€ÿm ƒ -€íÿý„ÿp € 6éþùûùùøûÿÿnƒ 0:*ïÿþ„ÿm%&*-0242Mqh\XH:95/0mnic]cDœÿÿƒH`NKB>=>BCCGHD=989:<=/./0*4mÿÿ“EaaihaUH:257:?DGHIHGFIIFATN84A:37Ê1 ”@Gnÿÿm98M;:ID==:28Ì1ŸDJDmÿÿj:"??N.?3>927È-c67 (lÿÿn`u (1A6C636Í8'9<>"-lÿÿlG€x9C!"B5452;‰{)+! /mÿÿl?v…] -562.58%>9/lÿÿ]p1Q}q{T($$!DŽbp2$pÿÿl0SxyuX1%&"$ =N{qhd"'lÿÿÛ¯9/B…lsN(!#0gsB? 'šà€ÿ÷(%4Eu€O %&\D d]^$('Öÿûÿüï.2D}N ' $9kwC +," Ìÿûÿÿ÷ %DH!&!":)/0/+&'$ ×ÿûÿÿõ%D(""# 8:,! Ñÿûÿÿ7ö  $B*#%#!&AD+ !"db^acbäÿýÿÿö!5!" ($ðÿþ…ÿö$ %*&&%#%( èûöùøøþÿõ íÿý…ÿøI+2/101.5DD@5./101->ðÿýÿþþ€ÿÿ‚* $‚#"$4i*!#"$ 1ïÿýÿ€þ€ÿm ƒ Díÿý„ÿp € 4éþùûùùøûÿÿnƒ 3B+ïÿþ„ÿm%&*-0242Mqh]YI:85/0mnid]cEœÿÿƒHaNKB>=>BCDHKJ@989:<A08)6KLA87=EA{[Nfm‹¦ÀL"xÿÿt3R0>9OK44A861ÀùŒäדŒÛ§ÑÄXFlÿÿjH3I67FA<>851‰¾ï˜âԑؼɌTM lÿÿgL">=H,92?760„»ïŠáȆžæÕ{<,; lÿÿkxv &0?0 C47.¸ð¾Þ“tvO@6=jÿÿjcy<"B A46/mÆÜµf1 8{»òY9jÿÿk^{‚ƒb 3:650ce.C}ÂöÿÿúN<jÿÿ0pNT{r}[!<534HˆÆôÿþÿùýòJ;oÿÿnW!O{xyW:855-qÍõ€ÿ)úúÿüÿã7;kÿÿܬT]@†irY266*ÌÿùüúþÿþÿûÿÊ<2šà€ÿ÷)=X?qY456)Çÿúÿþ€ÿüìµ]>+ÖÿûÿüïCT!>~V347(Èÿúÿþýöá²r9)/Ëÿûÿÿ÷ 9e M338&°ÿøýñ×§lA383 ×ÿûÿÿõ 6d%ðÿýÿþþ€ÿl8mk-Ä.à  p n¼ê²d 3q„“Ÿ«¹ÈÑÙëÿþÿÿÿøê×ǯšiSõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþü, âÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿòÏÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿß ¹ÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿÍ ÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿ¸ ˆÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿ lÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿöÿ‰ Tÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿn;ÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿX(ùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿ?êÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýü,ÃÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿðDûúþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿ÷ÿÙJùûýÿþÿÿÿÿÿÿÿÿÿÿþÿÿÿùûùÿF÷ûýÿþÿÿÿÿÿÿÿþÿÿÿùùÿÿÞh@ôüüÿþÿÿÿÿþÿÿÿøúÿÿÒ_ :ñüüÿýþþÿÿÿ÷ûÿÿÆQ5íýûÿþÿþ÷üÿü¹C0êÿöúöýÿù«7+äÿýÿóœ+"¤»ƒ"it32‘x€ÿüÿÚ|~­ ~y¯ÿÛ}{€~“€}†öÿþŸÿþÿùÿ³¬ \ñöüª• ëÿýŸÿþÿúÿº ª   4ûýÿõÿw  îÿþŸÿþÿùÿ¶«Løöÿöÿ¡‘íÿþŸÿþÿúÿ·ª8üõÿðÿ‡ îÿþŸÿþÿúÿ·® µÿôÿå&‘îÿþŸÿþÿúÿ·¯ šÿÑ1’îÿþŸÿþÿúÿ·­ ;Ì[“îÿþŸÿþÿúÿ·­<›G•îÿþŸÿþÿúÿ·¬8Œ@–îÿþŸÿþÿúÿ·­ ;? ”îÿþŸÿþÿúÿ·«9‚;‘îÿþŸÿþÿúÿ·© LI%€ŽîÿþŸÿþÿúÿ·§8`}”¼ÔÛÏÅ¥†sH €€ˆîÿþŸÿþÿúÿ·—‚‚€ Camq…¨ÇéþúÙ®sfcN> …ïÿþŸÿþÿúÿ·…‚„ =\XV[eu“ª¿ÏÉ´”s_URORB# †êþúû‚ú„ù‹úþƒÿ þÿúÿ·D  %')+01M\QTY`^fku†Ž†}kbYQKH@>A;;88612*'! íÿüþýýþ›ÿþÿúÿ·'(*,02368:;;<€> =?@=??:NVY`^``_`aabc^`_XVRMFC<735€9:<=;::;:8821++^ZVQJK€J0HGFDDEEGHHIJJKJHHIHLEÊÿûÿþÿÿþÿúÿ·,-347;:<<>?>>=>=€<:89€:B;:9;:>?=AZlxƒ{xvqokffb_`]WPH@<946<:;9::96866789;8;/-.-+*((# еÿúÿþÿÿþÿúÿ¶1@?==;:98987897€8998::‚;<;=>>€?aD?@@I`ny…‹ŠŠ‰†|umihb_YSNLB>8?@??=?<;:;:8;<;987===:6710) ¸ÿúÿþÿÿþÿúÿ·=GA>=<<‚:9:;::;9;<<;<€;8<=>@?;<@DJEOX`ny„“ª·º´® }rb[QIKG=??<=<>=99::899889656344€21€0-/0112123463$·ÿúÿþÿÿþÿùÿ·FNLOLJGEBA?=<;::‚9589:;:9;:<;:;9>><<@;<>BDKSTXiq‹¢¼ÓÚÖŶš…kaVJEDBA??=::;;€9678565€6 5564545445†4,324764¶ÿúÿþÿÿþÿøÿ¯UNXUTSRONLIGEA@?=;;::9:€9:9ƒ:0<;8>==<;::9-89889:8:9:><=>>CMRamšºÊÌÆ³”w]QH@>;:764556534454€5€6€7899€:;ƒ:6988662/1¶ÿùÿþÿÿþÿùÿ·2PLRSVY[]`a``^[ZWTQNLIED@?><;::998€9 879:9:;<;@CQYlŒ¥¦•fYOA9;7576766€5€677889::;==>>=>>?>€=<;;€:98€6€30-6³ÿùÿþÿÿþÿúÿÀ6KHMOPRUXZ^`degfdca]ZXTQNKGDC@?=<;€:898977867??AKO]\VMB<976767789::;<==>@AABBCD€CBBA@?>=<;;:88765€43€2u1,7±ÿùÿþÿÿþÿúÿÂ5HCGIKNPRUWZ]`cgijkjihda_[XTQMJGDA@><<;:998778898:86566799:;;=>>@ACDDFGHIIKKJKJIIHGFEDCA@?>=;;99765544€3‚2€0=,5´ÿùÿþÿÿþÿúÿ¿1E@EEFGILNPTVY]_cfimnpqppnihc_\WSPMIFCA@><<;;:;€<=?AB€C$FGIKMNPQQRRSSRQQONMLJIFEDB@?=<;997755€4€32€1?0//0/0,3¹ÿùÿþÿÿþÿúÿº-D>BBCDDFHKMOSUW[^bfhlosvxxwuqojgca_\YTNIFC€BCHLPUWXY€[\]\\ZYXUTRONKJGECA?><;:98765€4‚3€211‚0ƒ/a0,2»ÿúÿþÿÿþÿùÿ·*BDFGHHJLMPTW\_bekosv~Š•²ÒïöÒ¥¢·§}hl{Їzn`UPLHEBA?>=;;:9987766544322112„0€/..‚/00ƒ/../*3µÿùÿþÿÿþÿøÿ¯B6:20/05:>ACDEGHIJMOQSW[`bgkszˆ¤ÉðýÝ®ÂìËŠd€\ VQMGCA?>=;::9€876554432€1‚0/.€/‚0/.€/Q--,-/*3³ÿùÿþÿÿþÿøÿ°C284360//14:?BCCDDEGHIJKMOQTW[`gny¬Ðݽœ¹Þº}XNKIFDB?<;:9876654€3‚211ƒ0ƒ/0€10/.,,-..1246440+€,"50)3±ÿùÿþÿÿþÿùÿ²A688#.3520026;@B€D&EFHIIKKMOQSZ`kzŒ¨°‰ž²—mRFCA?=::8765534€3ƒ211001ƒ0//0/.,++)-/4;9;;==€?;=<==?@D;+/266/)3²ÿùÿþÿÿþÿùÿµ?6=3EH-?=7766779;=ABDDEFGHJJMOSX`hkgahlaOB<98654‚3€2€1€011/€100€.M-..0168:@DBKIBN3zÿ‹.C=BCEFJJQ@+4575-)1³ÿùÿþÿÿþÿùÿµ <88:+ "C4WH5;8:8877<9753€2‚1€2320.1-,,-/1369;B>@B=@=<;^=?@BCEGIQHiY€?NLPPRW_brM17584-*0 µÿùÿþÿÿþÿùÿ¶69>:HZeJ"??Tu- /4;=:786;38482,+-¶ÿùÿþÿÿþÿúÿ¸+==B>R`ŽsDCK9F`2/87:89;BBCEDDCBA<:63322€1.:?=><;;€98?3s‚ˆ_FKJKLPRTSTUTRS[`iv—k]j}~ƒ|nVA+,76561,+)¶ÿùÿþÿÿþÿúÿ¸(;853321101.9:9<9 '0?@ABBA@?=;853211001.;BB€F'GGIKTKQ¤L}Ôr`c`bu†œ•‹›£œœ”‘ˆs_P<)Þû€< 365560+-"·ÿúÿþÿÿþÿúÿ¸?@>><:74321€051-=HFKLQTUUQLOÇ—Paª¿¦Ÿ¬œ“——‘‚mXE*"òò+ €5u65.+-¸ÿúÿþÿÿþÿúÿ¸9HJR3lgg{C+D°V>€',GF+8?>?@>=<;97422100/1,?OTXVRORUf†°ŒWgaTbrŽ”‚yfR;"  )ùâ = 64653.+-¸ÿúÿþÿÿþÿúÿ¸7GGVBf`&'L}F4DZ2iMb#9L?;>=€>9<<;76421100/1*KZOX]n†˜œjdur‡–ŽfN3   %þÅ  ‚5 63554.+,¸ÿúÿþÿÿþÿúÿ¸4EEVH“eD[/x?N"N—U'.aTÞJ€4'=<==><<;:76431100.2(R||”šœ‘‰Ž…¡÷ç6   € L :ÿ»    63554-,+¸ÿúÿþÿÿþÿúÿ· /BLKi˜DUS1u"N7C†HF4ZZ}e3><€=,;::85331100.1*M„ ž’‹‚yfW;'~ÿÚ#  €W Pÿº   84454,,*¸ÿúÿþÿÿþÿúÿ·-@JG^\GB7|'S:Y$}<=<=;:975431€0.0,Bjr[J2 ŠÿÑ" ƒI  Oÿ¤   $73652,,)·ÿúÿþÿÿþÿúÿ·%EDL7B‰zp)‚'Y7Q$}3\4wsO+ 0>;=<<;:975321€0.1+B:  ÿÅ †w   Jÿ   &73561,-' ·ÿúÿþÿÿþÿúÿ¶#@BG=6HW‘qŠ8L4\/q-m> fr=H.=9<<;::97542110/.1+B6  Žÿ € …  eÿŒ a *74450+.$·ÿúÿþÿÿþÿúÿ¶>>E<1UBiZšˆh9W5h)fi!Rr5\*=9<;;::96532100/.1,?4  ŠÿÀ ƒ €  ‡ÿ‡ = ,54560+-!·ÿúÿþÿÿþÿúÿ¶8€;::96532100/.1+A1   Œÿº  € € Œÿf >/5355/+.¶ÿúÿþÿÿþÿúÿ¶:B>?#7`-lQ7o|k[U4Z-c… %>8;:96432100/.1+C8   ÿ¹ € ‚  ‹ÿK 06€4.+/¶ÿúÿþÿÿþÿúÿ¶8€?!-h*_ZGW$OYMK:z… „ ">8;€:986432100/.1+?: €  Šÿ¶   „€  šÿH € 147-+/¶ÿúÿþÿÿþÿúÿ¶4CAA$*l>VWFf+<_Nuy;c^ z>8;€:98643210//.1+@5  ‚ ‹ÿ· € „ ±ÿI   €4M53-+/¶ÿúÿþÿÿþÿúÿ· 6@C).„CJN1Y:FT=Qq8;::998543200//.0+<8 ƒ ‘ÿ¹ ƒ ‚ ¾ÿ3 T 64564-+.¶ÿúÿþÿÿþÿúÿ·!)2)4~OM%ZDEMHeK=>„kMN =7;::988543200//.1,>7   ‘ÿ· ‚„  Àþ!  € 75643€,3 ¶ÿúÿþÿÿþÿúÿ·## GŽ^J#hbDJ  €  ªÿ™ „U  *ÿ¼(99@?CGMLI:-·ÿúÿþÿÿþÿúÿ· +*%3R%&k~ªE’ zPUVj9p;<59€87643210€/.1+<6    ¬ÿ– €€ h bÔÿ“LEAFIE@4&·ÿúÿþÿÿþÿúÿ·(+,Y2Iay I(Ž0^2XAnUZ 18778866433100//.0,:;  € ±ÿ”  € V !8bœéÿÂ{^QC:. ·ÿúÿþÿÿþÿúÿ·)2+H,y"^REYxWU42ncF3‚766432€0//.0,9; €€ ) ·ÿ“  '9FRuœÐßÇ_>+6 ·ÿúÿþÿÿþÿúÿ·(7!qP2[• 4A?bsyWC ip, 18766432€0€/0-52( µÿ‹  !+2;JUh{£·°’Z,„A ·ÿúÿþÿÿþÿúÿ·(6%gy}~K#0llÌHA1za0867766543210/.0-73  ¹ÿ%.6;9BM[ozƒoQ)‡9·ÿúÿþÿÿþÿúÿ·&3-V¡³†’04PqSŠ+>a|"h/757€6533210/0-72 €  µÿ‹*03647?I€R B5Š€5·ÿúÿþÿÿþÿúÿ·%00I¡”—®K JS :qpOw.856533200/0.4- €  Àÿ”1<8764;A?D=, Š€5·ÿúÿþÿÿþÿúÿ·%.46¢¥‰¨˜ +L7RN5M2|;_*(846€543311ƒ/.2-'5L`v‘»éüç¤iI+Š€@·ÿúÿþÿÿþÿúÿ· (3(a¥•­§šž©£Tp7kq=kI%946€5432100‚/.174AIS`}˜ÀÐÞ²k7€‡N·ÿúÿþÿÿþÿúÿ·&0'X¶ž±¦§|¢ ¢rz]r  J#8465€43311ƒ/-2?CHWqƒ’˜Ža7 ŠMd$·ÿúÿþÿÿþÿúÿ·),*E¡®µ–­…šy—¦šLu I:":35432100/.-0FN`deU=! Š!'¼N€< ·ÿúÿþÿÿþÿúÿ·%*+>œŸ°¨‘ލ£¹¢FM…0[2P!/4744€3210/€./0/4OF:+Š3'z’5ÇÌ( €Y ·ÿúÿþÿÿþÿúÿ·"')6‰Ž›©²œ«…–Ÿ¯’y~„GžK@",366532110010.+%""ˆƒd‹k EY‰U·ÿúÿþÿÿþÿúÿ· $%/z‰œ¡®¦‡›žÆ€Go¯S¡0=&+.00/-+'$ €†€‚.˜(6bkL8lw!€@·ÿúÿþÿÿþÿúÿ·"3V‡‘˜“‘¶˜‹¡swdªh€ˆ€[% VÑ 8ªaW^=$™"Ú3·ÿúÿþÿÿþÿúÿ·  "F‚ra§Œ¡ §“Ÿ†‚w¬TÎ?L0 €€„‰_QªA6ÿŒH{=î¢Ù.·ÿúÿþÿÿþÿúÿ·  @mhC³Š™“¡¥©‰‘g¹uµ)b ƒŠ…€]!"(%1hÐkö/µŒ{o·¿§-·ÿúÿþÿÿþÿúÿ· 1 VMTc—›œ·š”³zÇ1Šk;  ‚ˆ€`:?)Ip 0¥®]„ŒÄYÓHÅã26·ÿúÿþÿÿþÿúÿ· 0L1O§€š’š°¡’ªÊ?»Cmz)„ˆ€ƒ"&hCÊ—"FõãQq²"‹­‡sæÙP7®€;·ÿúÿþÿÿþÿúÿ·9%Db ˆŸ­©ŸÉV¢\lfÓ) !†€†a%ˆŠˆži…¡„eÑ«R™õ,!ÝíL8€ ! ·ÿúÿþÿÿþÿúÿ· D~vŒ’hŒ‹«™³eœn‚PʼniB(…†#"²„ podnfÅ—y![Ë3“øK!#€ ·ÿúÿþÿÿþÿùÿ¶  3&2te{P™„Ÿ—¢©x­qvO·.¶‰#…&!…N Ž{ #ŒXšC¾h™í%d› ”!€‚A ¶ÿùÿþÿÿþÿúÿº$%&'9F5¡?—a¢‹Ÿ¬”ª‹tUÆ5­&†'ƒ#5¹"4_€ -e †àÈ{(¹'!51%ƒ ºÿúÿþƒÿ-úíîíðêñH'#%'"<˜O\s¾s–³•¸‚sf³Pˆ#…,‚4!T»2 kö{yOª[Dg. 'U {ÿæòííúŒÿ"ûÿK%$&&3 JM|}¹Š}f–¢Ÿ~`²a‘Œ!€„(€  QÅ, lÂ| ¦°k ŠN%c !ƒÿöÿþ‰ÿ)þýþýÿùþI !%%$&`–«L–’ˆ§}£rŽœ€…*)'M!#JŒ±ƒ‡’g±Ó’-')€"ÿõÿýýþŠÿ%þÿúÿG#!'&(*@}²i…M–—y§”±‡‡§&…5C¬C $N~λš3–ÜÀ.€…#€ÿöÿþŽÿúÿI€!'&('% 0T7Uš3–Žnª’šž“7…FP»Ù!!" 9f»Srƒ §°«€€€ ! #~ÿõÿþŽÿ#úÿI ('(( 6¥]…¥o½¦‚¯œ=‡=M‘ïT  'Sa«ÖЏ“R…'$&# !! "~ÿõÿþŽÿ$úÿI(**(#  ‰N™skȳ|‹•žQ!€…>L!H„Ls¹S6›˜Á)@z"„ &(--(''&!#~ÿõÿþŽÿ(úÿI+**+% ^†pg´ºk†šuX!†BKCÁxfP° í‘ OI""%(--/('% ƒÿöÿþŽÿ úÿI,':pSr×rvqj!€…OMjqs£hÚ+<±p %ƒ !'**+-1+&"†€ÿöÿþŽÿ$úÿI,--,)>[JׯƒZš`k!€„KN0ÿ«<ž“lp]0'+&.,+2.&„ €ÿöÿþŽÿ#úÿI ,-.+04*+M‰‡L¸Bi €…*4:(b­\°+“¬t; ""#*-,-.20&†  ÿöÿþŽÿ#úÿI +0//-%I›¿@^!€†&+'#Z¥ˆ¼•#$&+2,,/10"ˆ  €ÿöÿþŽÿúÿI€ ,20/+(K¦GC€„#$ -/¥Ê##'.-+10./+!†  ÿöÿþŽÿúÿI +2110%(“V €ƒ€!$UÀ" &*,-+,1//(‡ €ÿöÿþŽÿúÿI‚ €+5122(  )“…"o#+11/-,.1.%€‡ €ÿöÿþŽÿúÿIƒ +502/+f"‚ƒ )).33/130+ ‡  €ÿöÿþŽÿúÿI„(5010, ƒƒ)0/4128343(ˆ ‚ €ÿöÿþŽÿúÿI…)422/-ƒƒ!-B;86:4-"‚Š ~ÿöÿþŽÿúÿI‡%3120.…ƒ%,469::><4/ˆ "   ˆ  „ÿöÿþŽÿúÿIˆ#52410" „‚‚(2799;=9:+ˆ yÿõÿþŽÿúÿI‰#54410%„ƒ6499893$…|€~€€Š€~„t¿ÿúÿþŽÿúÿIŠ!6452.‚ƒ€$63:71"„ ôÿþ¨ÿúÿI‹ 4642‚ƒ‚ 345,‚„  æùõ÷öõû’ÿúÿIŒ 354 ‚ƒ€ $4&‚„  ðÿþ¨ÿúÿI 15 ‚ƒ‚€ƒ íÿýÿ‘þ“ÿúÿIŽ 1!„‚… €€îÿþ¨ÿúÿIƒ‚‚„†îÿþ¨ÿúÿIˆ‡„ˆîÿþ¨ÿúÿI“‡‡ƒ †îÿþ¨ÿúÿI”ˆ„ƒ ˆîÿþ¨ÿúÿI•†„‚ €Šîÿþ¨ÿúÿI•‚…‚ îÿþ¨ÿúÿI—€„€  Žîÿþ¨ÿúÿI”ƒ€€ €Œîÿþ¨ÿúÿHš „—îÿþ¨ÿúÿL ™€ €îÿþ¨ÿúÿIœ  ›îÿþ¨ÿýÿ˵º·œ¸ ¹¸µ·¶¹»¸¶µ¶¸¹¹š¸·½ú ÿ€ÿüÿÚ|~®€}‰›“~~•€}†öÿþŸÿþÿùÿ³® #$%– ëÿýŸÿþÿúÿº ¬ A?>;E# ‘  îÿþŸÿþÿùÿ¶­ 41325!“íÿþŸÿþÿúÿ·¬ :6:4<‘îÿþŸÿþÿúÿ·®)90:4•îÿþŸÿþÿúÿ·¯1Q* ”îÿþŸÿþÿúÿ·­2z!•îÿþŸÿþÿúÿ·¯2u*–îÿþŸÿþÿúÿ·­4w.–îÿþŸÿþÿúÿ·­8x/–îÿþŸÿþÿúÿ·« 5v/€‘îÿþŸÿþÿúÿ·©>q5€ŽîÿþŸÿþÿúÿ·§#5=?DCJ@>=84%€€ˆîÿþŸÿþÿúÿ·—‚ƒ€€7FIEA><<;:99<=??6$ …€ïÿþŸÿþÿúÿ·…‚ƒ€ 8OKFCD€CA??>?>=;::<7†êþúü‚ú„ù‹úþƒÿþÿúÿ·€D ")++-44KVNPQQOMKJIHGGFFEC@=87;<><;843+)# îÿûþýýþ›ÿþÿúÿ·")+,-4678;=>>?@A@ ?>=<:54.-a\XRJ€KJHGE€D%EFHHIJJKJIHHGMEÊÿûÿþÿÿþÿúÿ·0077;€?ABCABAA?€>)<<=<<;:;;:;;=:@]nz„€}xwrokhfcba]XQKC>:646„87899:;;==€0 /--+*& еÿúÿþÿÿþÿúÿ¶5EFDB@?><;‚<„=ˆ>?HYm{†€ˆ‡„€ysleb^ZUOIC?<:‡<;;€:€?@,ABCCEDDB@>:;63," ¸ÿúÿþÿÿþÿúÿ·BNGDBAA@?‘>†=<<>@CGIK€M NMKHGFECB?>>==ƒ<;€<„; <989887766€54€5(676789;8'·ÿúÿþÿÿþÿùÿ·LUTVSPMJIFDCAA€?>‚=<…=>=>==>=??@@AA@?A@?‚>€=†<;„:9:‚9†:‚9€8'9<;7¶ÿúÿþÿÿþÿøÿ¯"\Va\\[ZWUSPMJHEDBAA@€>ƒ=<<…=>ƒ=>€?@>>?>><=‚<€;::“9‚:;;€<€=.>>=:878¸ÿúÿþÿÿþÿùÿ±2]W_abddcb`_\XUROMJGECBA@@€>=<=ƒ<‚=ƒ>=€<€;:98::9‚:€;<<=€>??‚@AA@??€>4<;;735¶ÿùÿþÿÿþÿùÿ·8VTZ[^adfiihhfdc_\YVSOLIGDCBA@?>>=ƒ<;;<<‚;::€87€8†9ƒ:€;<<=>??@AABCCDDEEDC€BAA€?;=<<;:9881:²ÿùÿþÿÿþÿúÿÁ??@ACCEEFHHIƒJ9IIHGGEECBBA@>>=;;:998876560;°ÿùÿþÿÿþÿúÿÃ:NIMOQUWZ]`behlqrtqmjhc`\XTQMJGECCA@@>==€<‚;€<€=.>>@AACDEGHIKLMNPPQPPQPPONMLLJIGFEDBA@>>=;;:9988€7?65544509´ÿùÿþÿÿþÿúÿÀ7JFKKLNPSUX\^aehlorvx{|{zwsqkgd^[WTPMKGEDAA€@?AABCDEFGIJLNPRSUVXY€Z[ZYXWUTRPOMKJHFECB@?=<€;:998€65>4433408"¹ÿùÿþÿÿþÿúÿ»3ICHHIJLMNQTW[\_dfjoquy~ƒƒ‚|xtpljgda\UPLI€HJOTX\_`b€cdeff€eca`^\YWTROMKHHEDB@?>=<;;:€9€877€655€4„3a406#»ÿúÿþÿÿþÿùÿ·/HAGFGHIJKLMOQSVZ]aeimnsy|‚„‡Š‹ˆ‡††…‰‰via\WVX_nƒ“‡}wsnmjgd`]ZWTPMLIGECB@?>==;;€:99€8€76€5‚4ˆ3[/6!¹ÿùÿþÿÿþÿùÿ´-F8@BEGHHIJKLLNPQTX\_ceinsv{~ƒ‰‹’› ®¿ÇÄªŽƒ‚{ojt¯¹±¢}vmf_[XTPMLHGECBA@?>=<<;:9€87€6€5454323/7 ·ÿùÿþÿÿþÿøÿ°$I996:?CGH€I>JKMNNPSUX\^dgknsy}‰–£Áæýÿ洱ɵ‰qu†–“…xi]WRNKHGECCAA??>=<<;::9987€6ƒ544†34‚3?213.7µÿùÿþÿÿþÿøÿ® H==<;;::9989887665‚4€322€34X32211223312024-7³ÿùÿþÿÿþÿøÿ°H8?::=75479?EGIIJKLMMOPQTUY\_dhpx…œ»ãñϪÊñˈaUQPMJGECA?>=<<€:998766556€5ƒ43456633201123569;9940L:5-7±ÿùÿþÿÿþÿøÿ±F:>=(39:8558;@EGJJKLLNNPQRTVY[bit†™¸À«•¬Ã¤wZLJGDBA><<;::99€8€76566‚5„45€47321..-139>=?@BB@?>;<81229<3-7±ÿùÿþÿÿþÿùÿ³E>?=7!7;<;€9=BFHJK€LOOPQRUX\cm}–‹~Œ˜…ePFC@>=<;€98€7ƒ6€5ƒ4€544321_36;DLXUv¯d?A<><==>?CDHEKKFN4{ÿ‹.C=BCEGJJQB089<82-6³ÿùÿþÿÿþÿùÿµ B<=?/ "C4VH8@=>><;;@AEILLMMNPRVZ_cea\__YLC?;::8€7€65€677€41012368;>@@B?D@>?<::;9<B<=>;?=ADEILMMNQTW\]Z€W PGA<::8877‚6€5u320/0468BEEBFE@B>=<<;;=?@BCDFIQHiY€?NLPPRW_bqP5<:=80.4 µÿùÿþÿÿþÿùÿ·;?C>MZeJ"??Tu- !28@A><<:?@EIKMORTUSQQPKD?;:877€6{54432359=;:;=?@BDEFGIKMPPS=z›FÏ™kwt™¢±]2=9=60.3 µÿùÿþÿÿþÿúÿ·5BADH[hp HZ6Q9:A:<:8<>@DJJMONNLLKFB=:877€54579==?JKKHFJ6hÿíC8?@CEDFGHILOQTSSRQS^Ès\tœ¡«­°ª›x\@9<:<6//0¶ÿùÿþÿÿþÿúÿ¸.BAFBT_†sDCK9F_53<HHJKKJHHFB?;98765452=EA@==;::98?3s‚‰_FKJKLPRTSTUTRSZ`ivœ˜gZjv‚{lP>%0;:;;5/0-¶ÿùÿþÿÿþÿúÿ¸+BFIEA![Å5oL#';q#+779:FGHIIGFECA=:9765545.2<=9<<>ABEFCMAnIgW=QMRTOORW[_k{‘‘œª¡—Š~˜Àà¡;$P5;:;:400+·ÿùÿþÿÿþÿúÿ¸"BGKO/8Z@ee]FŠX>9 +5DEFHGGEDB?=:87655452=EC€F.GGIKTKQ¥L}Ôr`c`bu†œ•Œ›¢”‡qWS'ÿÿ‹7:t3/1%¸ÿúÿþÿÿþÿúÿ¸ @IJ\(l)]ƒE fWÖ:9q"RI8DDEFFEDCA?<977554351BLFJLPSUUQMOÇ•P`ª¿¦ ¬œ”——iQB" ¡ÿÿ€;:53.1"¸ÿúÿþÿÿþÿúÿ¸>KNU6j`fw>+D¬Q>€',GF+>DC€D+BBA=;976644350DUUXURORVg…¯Ž[gbRasŽ“‚x`N7u ¦ÿÿ^<8::92/1¸ÿúÿþÿÿþÿúÿ¸89:800/¸ÿúÿþÿÿþÿúÿ·1ENKc[AC6v'M:Z$|9V;cgd7DACBBA@?<:976€4242BnrUG* V÷úÿ‚€…  Øÿõ<€H >8:9600-·ÿúÿþÿÿþÿúÿ¶)IHQ;:966€4240>3oþùÿ¬‹ºÿÜ‚=&=8::500+ ·ÿúÿþÿÿþÿúÿ¶&EEJB7IQo‚:G3]-p-f> fr:F1C@€B@@>;:875544250@- €Wýùÿ¨†ƒ ÛÿõQ€R*<79:4/1'·ÿúÿþÿÿþÿúÿ¶ BAIB3V:fY”Šc5Y2g)_i!Pr2Y.D?BAA@@>::866443240?- 6ëþÿº†€ :íÿÞ‚b-:8:;4/2%·ÿúÿþÿÿþÿúÿ¶ADFB+S@o>^iuh1[#Zb0Rtd+D>AA@@?=;:865443240>.dúüÿÕ‚:÷ÿË„F2:8::3/3 ¶ÿúÿþÿÿþÿúÿ¶?DAD!$4\*aT5j}jYP0[-`… z)D>AA@??=;:7€543240?0ƒhÿ÷ÿŸ…a)áÿ¶3;8992.3¶ÿúÿþÿÿþÿúÿ¶=CBC$+c&S\FR$MZIG:p‚ ~ %D=A@@?>=::865443341=;9865433240@.ƒPôýÿ“†:öÿ‡€ :€9771.3¶ÿúÿþÿÿþÿúÿ·(Om=^=h.C=€?>>=;98654€341=4‚ÿöÿ‘ƒ‚{ûÿ ‚>;9:;81.3¶ÿúÿþÿÿþÿúÿ·#+28-4{KH%UC?EJdC=?„jIJ B<€?>><::75€43341;+‚ hÿøÿk‚‚ |üÿ€€>9;97€04 ¶ÿúÿþÿÿþÿúÿ·!--)& HZI#gb=<:98554€340=* rÿõÿ}€Ygÿÿ™=<>;60/0¶ÿúÿþÿÿþÿúÿ·--)*Xˆ|?WqB8=ZBx\%.c‘:A>==<:98554€341;- kÿúõ9 †ÿÿv€>>K=76602+·ÿúÿþÿÿþÿúÿ·,-+*!#a¹E>}N=U;ƒj"#s‡˜VA<€>==<:97544€341<+ Qÿ÷ÿ…‚€Kÿÿc 1F\gd_SJ8,(·ÿúÿþÿÿþÿúÿ·,.-#3;W+ %1„U.>'‡ q5w/yœm€= >==<;:87644€3419- IýüûR‚€L•ÿúE"5?KRW^_`_R@&!·ÿúÿþÿÿþÿúÿ·,1/+ B*;\…\ <s :qR ˜·´?‚=<;:86644€340=4ƒƒÿ÷ÿ|„€[‰ÿÒ6*>CDJPXXUE6&"·ÿúÿþÿÿþÿúÿ·*12-3Q&%k}¤E tPVRh8j:@:=<==;;987544€340;0€  ÿùþB‚L]ßÿ‘JGFKPNIjPW 6=‚<;987544€341<2 uÿöÿ†‚U /XšìùµxXQHA6(  ""! #·ÿúÿþÿÿþÿúÿ·&1:5P+e"^SEYwUS40l^B8ƒ<::87544€341;2aÿ÷ÿx€$5?Pp•Áн‡_D/#€"  9"·ÿúÿþÿÿþÿúÿ·#/>-uL)P‚ 4A?bqtQDei* 6<;<<€;987543427)…iÿýï-(2=GQbt›®¨\1 !"#!€ ! !€ 6" ·ÿúÿþÿÿþÿúÿ·!0bw d3<:€;::97654341:,*ÿúø9!.3657=JOOQE7$$$"! !""!  !!€ !€ 5·ÿúÿþÿÿþÿúÿ·/68Lˆ„¡? JS :rlKr2<:€;::977€4€3418(€iOûÿÈ1,5543ŠŒs‹‹„*L;7,$ !$#! !""!€ "#!€ E!#" !·ÿúÿþÿÿþÿúÿ·-4<6cny„¡v8LB#;¦^ /=9::;::97654‚325) 1V†Ùÿï´{ZLA60(! ""! €"L! !#$! !!"$" !·ÿúÿþÿÿþÿúÿ·,4:7PpuŠ}¯s$[;3ID6L3{<[(,=9;:87644‚3250$1IYl‹©ÕâË”dF-$ !!"" €!Q"!! !#$  ! "#$""·ÿúÿþÿÿþÿúÿ· (194S{v‹‰†h‘šƒC]9Y]>kI)>9;:87644‚325;7>CNWlЬ›e:# !"!! €! ""! !## €D#!"#"!"·ÿúÿþÿÿþÿúÿ· '/71O‘{ˆ†b‘‹_s HWJ&>9;€:987644‚326C?FSbv‚ˆ|[8!""„! ""! !#$ FU%"$$#""·ÿúÿþÿÿþÿúÿ·$144=}’t‡g~an‡’Œ?]  I:&?8:98664ƒ315FN[^_QB'"%#! ! ""! "## €i  ¸>%"$##!"·ÿúÿþÿÿþÿúÿ·".169vx“†moƒd{|ž˜H>d1S2P!79<:998765443223437JG<.# %&#  !""€ !$$ €i" n…ÃÇ%#%##!" ·ÿúÿþÿÿþÿúÿ·,044kq‰x‡ir—yoaC‚K@'*39<;:866544542/+)' !#$" „! !$$ €hƒuZ5I|L&#%%$!! ·ÿúÿþÿÿþÿúÿ·*-1/eqv{‚iv€ŸrEj˜Jx /A#'%).2564410-+'$"! "#"! „!€ !$$ €”0uNaD/dl#&#D! ·ÿúÿþÿÿþÿúÿ·*+-5Hg|„to—~rl{bhcc¦ )&(&%%&&%$$#"€!"##"! …! "$# W JÌ¡NBS, Ž×; &#%" ·ÿúÿþÿÿþÿúÿ·*),.Hc`Y‡j~††qvlZo‹L«;K.)%'&€'%$##"!"„!€ "$# €WC£0%ÿƒ4ot /ë—Ø7#&#$$!·ÿúÿþÿÿþÿúÿ·)(+)FeaD–l€s‚‡lkTk–$†\)%&&'&&%€$€#ƒ" !!"!! !"## …_ XvÌ]ô"‡¬qc´¸ 5"%!$"!·ÿúÿþÿÿþÿúÿ· ''+)5 VKNU{sv|”|nfˆl¤)p[A (%&&'&&%$$€#„" !! !"$" €^', Bd ¨R{†ÀLÍ6Àß—!,‘&#$"#!!·ÿúÿþÿÿþÿúÿ· &)*) 1K6E€ivos’€s}œ6 (dy+ 'ƒ&%$$€#‚"€!"$" €c by3Æ 5õÛJg­‚¦~fäÕA¨ &!"##!"·ÿúÿþÿÿþÿúÿ·$**.:%Ca‚on}ml‡v‰£@DUZÄ* )%'&%$$€#ƒ"#"€a €|{’’\x›}[ΣF’òÙë>$p+$##!"·ÿúÿþÿÿþÿúÿ·!+-0 Eulf_ni~ˆŽL„QcH£€mB0"(&%€$##‚" €ƒ€9«u gf]ca¾’p MƉö?%!#"#!"·ÿúÿþÿÿþÿùÿ¶ )-123%3uRmQ~d|sŠ\†WXG–¯+$&%€$##‚"!#…#?…l†Q‘8¸]ë[” %!€#@!! ¶ÿùÿþÿÿþÿúÿº-9:;:9E8’@…G€h‡”{ƒiZG¦#“l-#'&&%%€$##ƒ"%…6%· SqqXÞÁrµ""Œ $!"!#! ºÿúÿþƒÿ)úíîíðêðL558:4;RMX™S–x˜hVQ6|k+#&&€%€$#‚"!$-ƒ4L²]öqp@¢OŠ6rc ‹E$!#"#) |ÿæòííúŒÿ$ûÿM')5566;GOqa§un`s}ˆeJŒEk)$%&€%$$##"# %&‚I¼]»s™Ÿ§e z=W # "##)„ÿöÿþ‰ÿ)þýþþÿùÿH+!,3537 [y¡uqLvmj‹i€Txw"'$%&€%$$€#" %)E>«y~…_¬Î…# … ",)'€$ *ÿõÿýýþŠÿ"þÿúÿF,$,547:!>d­etMysb†}“dn&ƒ%€$##‚"! %:T¶2CqË´‘# ŽÙ¼€ $)+)+-*&$*€ÿöÿþŽÿ#úÿI$#$,56561 0I5WŠ6{gWty‰xw3"&$€%€$##‚" #RoÌÒ'[²„Gd|  ¦¥ƒ(*&)+,+'"#+}ÿõÿþŽÿ úÿI&##+€56-7˜\t•\˜ƒ`{—~4!&$€%€$#„"!FeœéNFuU¦Ô {¶Š?—ƒ€ -,0+(++*$! #+}ÿõÿþŽÿ#úÿI%##)58761  Nr^£^mt„J($€%€$#"2!"!$J\8}=h´J"–“‹½.j%1860/11+#  "+~ÿõÿþŽÿ#úÿI%##*77681Z|k^Ž˜Qlu\J($€%€$#"€!-$P]3¼o[@ªëƒ>9(16791/.)#ƒ !'ÿöÿþŽÿ#úÿI%##)88984:kOs¡|Ub~WU)$€%€$#"!""#ackdlžeÖ5¦j€ .2125=50+! ‚ !€ €ÿöÿþŽÿ$úÿI%""(8:886>[ DǯZU}HY)#%‚$#"€!#]d*wÿ¥3•‰^kH€€ )4,624>9.' ‡  €ÿöÿþŽÿ%úÿI%#"'79;8=4*)PqE‘1X'#%%$#‚" ";/!  !€ !‚  ÿöÿþŽÿ%úÿI%#"%7<<;:/K™“.T("%%€$##"!0& Ozµ‹ €$-4<539;;," !!€ !! !   €ÿöÿþŽÿúÿI€$##%8><;94€N‹:B'#$€#"!'›Æ € ,861::885) ‚ !!   ÿöÿþŽÿúÿI%"#%6?==>0 +Q("%$€#""! €B¼&35525;892$  !‡    €ÿöÿþŽÿúÿI‚€#$6A>>?5 ((#‚$##"€!  €f /==7548:9.#‰   €ÿöÿþŽÿúÿIƒ $€#5B=><8d+!%$€#"!‚(08><8<>;5(!!‚ !€ !€  €ÿöÿþŽÿúÿI„ $##"4B=><9&#%$€#"!€"49A:;D>??1%! !‡  ! €ÿöÿþŽÿúÿI… $#$"5@>?:9 %#‚$€#€"‚!-JUD?A=@@=5% !„ !"! !!ƒ €ÿöÿþŽÿúÿI† €# "2B@?::$„$€#€"‚!+@NRGCBG>8+" !† Š ~ÿöÿþŽÿúÿI‡ €# !/@>>=;)$#‚$€#€"‚!3@BEGHNJ@;'  !† ! ')(!  ˆ  „ÿöÿþŽÿúÿIˆ "#$!.A?A<<, "„$#"! 3>DEEKMGI6$ ! ƒ !   yÿõÿþŽÿúÿI‰"#$"-B?@=<0#%‚$€#""! "#B?FGEGA-""‰ !}}€Š€~„t¿ÿúÿþŽÿúÿIŠ!#$"+A@A?9&#ƒ$#€"! #/DAHE=+ !† !!  ôÿþ¨ÿúÿI‹ !#$!)@BA='#ƒ$#€"‚! !@BC8' ! ƒ !!  æøô÷öõû’ÿúÿIŒ  $#"'?A@)#%‚$#€"€! "0B1$!!† !  ðÿþ¨ÿúÿI  $#"&=C*#ƒ$"ƒ!(! !!†  íÿýÿ‘þ“ÿúÿIŽ  $##$=+"%‚$"ƒ!!" ‡   €îÿþ¨ÿúÿI $#"&(#%‚$‚"ƒ!‰  €‚îÿþ¨ÿúÿI$##"#‚$#€"„!† !  €„îÿþ¨ÿúÿI‘$"$€#$€#€"ƒ!‡  †îÿþ¨ÿúÿI’$"„#€"‚!ƒ !! ˆîÿþ¨ÿúÿI“$!$"#""ƒ! !! €Šîÿþ¨ÿúÿI”€$!#‚"‚!ƒ  Œîÿþ¨ÿúÿI—$!#€"‚!€ !!  Žîÿþ¨ÿúÿI”€$!€"!""! !  €Œîÿþ¨ÿúÿHš!€—îÿþ¨ÿúÿL ™$& %&&%$€îÿþ¨ÿúÿIœ  ›îÿþ¨ÿýÿ˵º·œ¸ ¹·´¶¶º¾¸¶´µ¸¹¹š¸·½ú ÿ€ÿüÿÚ|~®€{­Ÿ~}•€}†öÿþŸÿþÿùÿ³®HHK/” ëÿýŸÿþÿúÿº «  c`_\l3 ‘  îÿþŸÿþÿùÿ¶­ WRWSX8“íÿþŸÿþÿúÿ·« ]V\Sb2‘îÿþŸÿþÿúÿ·® B_S_U “îÿþŸÿþÿúÿ·¯Ae?”îÿþŸÿþÿúÿ·­1z!•îÿþŸÿþÿúÿ·¯4w,–îÿþŸÿþÿúÿ·­4w/–îÿþŸÿþÿúÿ·­8w/–îÿþŸÿþÿúÿ·« 5v/€‘îÿþŸÿþÿúÿ·©>r6€ŽîÿþŸÿþÿúÿ·§$7@BIKRGEB;7&€€ˆîÿþŸÿþÿúÿ·—‚ƒ€€7GJFDCBEFEA?@?@?6& …€ïÿþŸÿþÿúÿ·…‚ƒ€ 9OLFD€FGFFEDC?><::=8†êþúü‚ú„ù‹úþƒÿ þÿúÿ·D !)++-44KVNPQQONLKKJJIGGEC@>97;=><;843,,# îÿüþýýþ›ÿþÿúÿ·")+,.4678;=>>?@A@0?>=;944.-`\WRJ€KJHGE€D-EFHHIJJKJIHHGMEÊÿûÿþÿÿþÿúÿ·0077;>??ABBCC€BAA@€>=<=<<€;"::;:<;A]nz„€}xwrokhfcb`]XQKC>95377ƒ87899:;;==€0 /--+*& ‰µÿúÿþÿÿþÿúÿ¶5EFDB@?><;‚<„=ˆ>?HYm{†€ˆ‡„yrkeb^[UOIC?<€:;‡<€;::ƒ@AAB€C'EDBA>9;63,! ¸ÿúÿþÿÿþÿúÿ·BNGDBAA@?‘>ˆ=>@DGJMOPQRRPLKJHECA??>€<=ƒ<†;8€7665545,676889;8(·ÿúÿþÿÿþÿùÿ·LUSUSPMJIFDBA@@?>>Š=>>=>==>=?@AABBA@CDFHHGEDCA@??>==ƒ<‚;:9:€9::9‚:‚98)<;8¶ÿúÿþÿÿþÿøÿ¯"\Va]\[ZWTRPMJHEDCA@??>>Œ= >=>>==>>?@@€B DFGEDBA@?==€<;;‚:’9‚:;€<€=.>><9878¸ÿúÿþÿÿþÿùÿ±2]W_abddcb`_\XUSOMJGECBA@@€>€=…<=>>??>>€? ABBA><<;::’9::€;<==€>??@7A@@??>>=<;:735¶ÿùÿþÿÿþÿùÿ¸8VTZ[^adfiihhfdc_\YVSOKIFDCBA€?>=‡<…;<==<:‰9ƒ: ;<<=>??@AAB€CƒD€CB€AB??>==;;:9881:²ÿùÿþÿÿþÿúÿÁ;PNTVWY^`cfilmonmljec`\YURMJHFDCA@@??>==<€;:99:‚9€:€;<<=>>@@ACDDFGGHI„J8HHGGEDDBB@@?==;;:998877660;°ÿùÿþÿÿþÿúÿÃ:MIMOQUWZ]`behlqrtqmjhc`\XTQMJGECBAA?>>=<;<;;€<€=)>?@@ACCEGHIKLMNPPQQPQPOONMLKJIHFECBA?>=<<;€98877€6544509´ÿùÿþÿÿþÿúÿÀ6JF€K;NPSUX\^aehlorvwz|{zwsqkgd_[WTPMJFEDAA@@??AABCDEFGIJLNPRTUWXY€Z[ZYXWUTRPOMKIHFECA@?==;::99€8776€5I4433408"¹ÿùÿþÿÿþÿùÿ»3ICHHIJKMNQTW[\_dfjoquy}‚ƒ‚|xtpljgda\UPLIHGGJOTX\_ab€cdeff€ecb`^\YWTQPMKHGEDA@?><<;€:9877€655€4„3406#»ÿúÿþÿÿþÿùÿ·/HA€GJHIJKLMOQSVZ]aeimosy|‚„‡ŠŠˆ‡††…Љ€via\WVX_nƒŽ’‘‡|wrnmifd`]ZWTQNKIGECB@?>=<<€:99‚8676€5€434ˆ3\/6!¹ÿùÿþÿÿþÿùÿ´,E8@BFFHHIJKKLNPQTX\_ceinsv{~ƒ‰Š‘š ®¿Çꃂ{njtޝ¸±¡}umf_[WTPNKHGDCB@@?>==;;:€9877€655‚4‹323/7 ·ÿùÿþÿÿþÿøÿ°$I9:6:@BFH€I:JLMNNPSTX\^dgjnsy}‰•£Áæýÿ沰ȵ‰qu†–“…xh]WRNKHFECBA@@>>=<;;€:989877€6‚5€4€3€2€3€4‚3€23.7µÿùÿþÿÿþÿøÿ® H;A:756;?CFHI€JKMMOPTUY[`dikpt}„”³Üüÿð¾ÓþÜ–n€d^XSNIFEDBA@?>==<;;:€9€8€76‚5€4€3€2€35654321‚3O245-7³ÿùÿþÿÿþÿøÿ°H7>;;=76489?EHIIJJLMNOPQTUY\_dhpx„œ»ãñΪÉñˈaUQPLJGDBA?>=<;;:€987€6565€4‚3465331--.137:;?=<71323<4,7±ÿùÿþÿÿþÿøÿ±F9=<'39:8558;@EH€J#KLNNPQRTVY[bit†™·¿«”«Ã¥wZLJGDB@>=;::€9877€6‚5€433445@430-,+,25>ILOMMKGBA=<81349>3-7±ÿùÿþÿÿþÿùÿ³F=>=7!7;<;989=BFHJ€KLNOPQRUX\dn}•‹}Œ˜…fPFC@>=<:99887‚6€544334€5A66422../05=FOZcd„ÀrMI?=;<==@D=148><3-7²ÿùÿþÿÿþÿùÿµD9@7JI-@?"<<€:9=>AGHKMNPRTV[aiqupjqvjVHA><;9988€7€6554€5K7875320//025:AGMW`b_ZPP6{ÿ‡*A;BCEHKKRB09;?;2-6³ÿùÿþÿÿþÿùÿµ B<<>/ "C4VH8@€=;:;@AEHLNPRV[_cea]__XLC><:9€8€65544667€8"432.,-059>AGNNQNOJEC=9677<:vˆtIQNPR€SPTC7=<>91-5´ÿùÿþÿÿþÿùÿ¶=>@:L4!wj31^86=;==9<;99:=>ADFHJQHhX€>LJNNPV`JeuQ7?>?:1.4 µÿùÿþÿÿþÿùÿ·;>A@EIKMORTUSQQOKD?;9977€6{5310/17=GT[fm†ÜÂWPA=:89;=@BEFGHIKNPPT=yJ͘m{y„”𛛍\6@<@90.2 ¶ÿùÿþÿÿþÿúÿ·6@?AG[hp IZ6Q9;A;<;9;=@DJKMONNLLJGB=9877€5047;BHQ^_fd\PP:hÿê?4>?CEEFHHJLORTSSPNO[ÊoTw £¡¤£€š2–Q8@;@8//0¶ÿùÿþÿÿþÿúÿ¸.@>CAT^‚ysDCK9E_64€=;;=GHJKKJHHFB?;97765461GUMKFE=9656>1s„Š`HLJKKPSTTUTRPQX_m{–¢™cd|py’Ÿ«§¦¡…lkE:A>>6/0,¶ÿùÿþÿÿþÿúÿ¸*@CFA? ]¶,oL#';q#,799;GGHIIHFEC@=:87655451CB5::9  ,6DEFHGGDDC?=:77655361FLBGGHGGHJTJQ¤K}Óp]_]ax‹¡™Ž˜›“‘‹“¦«ÉùøÿMÿøüå¸Â¶–€}p>=?=?4.1%·ÿúÿþÿÿþÿúÿ¸ @EET(\)Yt9 eXÈ39q!RI8DDEFFEDCA?<98655436/KTDKKPTVWQLMÄ–Tb«Ä”¥¬ ¥‘ˆŽ“›ž›¦¸Í̤äÿóý62ÿûÿá³¥“sfmb<>??>3/1"¸ÿúÿþÿÿþÿúÿ¸=HIO5aP_k5 *F¥C>€',GF+=DC€DCB@=;97664437.P_SZVRNQRd…²“Y`^QWbm–›¨®®¢–~§ÎÛËÓöÿüÿ$Eÿøô¾®©—…ucS;>>?<2/1¸ÿúÿþÿÿþÿúÿ¸^L+Gu5'A‹K3iNY!:L?@CBDDCAA?=;88655437,\jLW\oŠŸ¢ŽeUq„‹¡½ÏãëÐÇÄ¿¦•¥™š´¼½»æÿüúLÿ÷üÛž˜°žebh8?>><1/1¸ÿúÿþÿÿþÿúÿ¸9FFUBU3`$j4= "L‘C!-aUÊ?&BABCCB|AA?<:87654527)h‘|”–˜–ˆ‚z‚¤ÉºVzÿüÿþÝÚØÔ¶Œ‹Œ†‰šœ«¹¾âýÿðlÿõÿÆ®åÓ„c‡q7B;?;0//¸ÿúÿþÿÿþÿúÿ· 3EKL_Ž=AZ&e ;6B:93Z[qX8CA€BAA?<:87554418)i ——’š¡½ëÿùÿ¸=ø÷þìÐÏÂÆÁ¬–ŒŠŽž·Á¼äüÿÝ}ÿùô¯ÀϤvgj7A;?:00.¸ÿúÿþÿÿþÿúÿ·2CKI_ŽX2H-h'<:Z#y2J: `cV7D@BBAA@><:876€4|18(l¥‡•¥¡˜×ÿüôÿ®PýýÿüóÖË딃‡’—¥ÑæÞÓ÷þÿÛ„ÿ÷õ«ÑÖ c{f6@:@9/0-·ÿúÿþÿÿþÿúÿ¶)FEN;Eˆhsh*B4R {.N2plE)4C@BBAA?><:876€4r18'oœy©Å¶½Óõÿùÿ™IûüÿøÕËÒÎÁ­­µ¾±´èé´ÀòûÿÕ™ÿ÷û¹¿ãÅŒi‡]6A=A8/1+ ·ÿúÿþÿÿþÿúÿ¶&CDIB7JCkr?80^)n,U> dt/=2D@B€A@>;:87554418'n˜u¦¶ÅÉÉÕöÿ÷ÿOüüÿùÝÑåóÙ¦¬§—¬¿¼¹µÜûÿÄŸÿ÷ÿǮǫ‚…N:@;:866€4|18&n’j’±Å¿Ïåéûùÿ´Nþüÿ÷èøùݲ›‡xstŠ “Âÿöÿ««ÿùÿÒ¼¿{‘S8?;@5/2%·ÿúÿþÿÿþÿúÿ¶@CEA*U3h:Lnxnj,U'K c/Pu\+D>AA@@?>:9865€4z18'l’n‹—­¨ƒµ÷ÿúÿªJüýÿèåÕ±t\c¤ÑêÜ¢Õÿõÿ˜Êÿ÷èÎÒÒµ‹omE:==<4.3 ¶ÿúÿþÿÿþÿúÿ¶@C@C!&,R&PY/^gU!F)\.Yƒm)D>AA@??>:98€5~4418&m•]—³º—{Êÿÿùÿ¤Vþüÿ쫊¿ßÙ¼§ƒ¦Òɽ¯üøÿŸÍÿú몔”’~~›H;?<;2.3¶ÿúÿþÿÿþÿúÿ¶=C@D%&V!BaAC'H‹]?;: ]| p %D=A@@?>=:9865€418&m˜p´ÆÇÄèÿýÿùÿ©pÿùÿùÚÕâÏÒÀŸ€|“Ãßݳ°ù÷ÿƒèÿÿᄌËÁpj@;<>>2.3¶ÿúÿþÿÿþÿúÿ¶ ;CEF(%X0<\BQ,/TQqo<ITd"C=@@??>=:9865443q18(j¦—Åά­¾èÿ÷ÿ¤lÿùÿùîßÏÂÇÁ®›zjuŠ”¨Úÿôÿ õýÿê«ÏÒ¤„€u=?<<;2.3¶ÿúÿþÿÿþÿúÿ·7BDF,-t64Q2D85@ALf~>R9 ](C=€?A>>=:985543318(g›y”Œ•œ°òÿøÿŠ\ÿûÿûäÊÏѵ“qq}‡¨²«¯êùÿXõþÿäÂÎÉ¢ra<€>7;1.2¶ÿúÿþÿÿþÿúÿ·4AEC2 4xA9(NA24M`4><9975€4s318&lšQ«ÓÜÏðÿÿùÿ‚nÿùÿüáÁª–}r‚™–ƒ‹œ©µÏúøÿTÿüÿèÄȶ•lj`:?>=90/0 ¶ÿúÿþÿÿþÿúÿ·0ADB: HŠN<$da/3?P4IOekjJ B|=<9985543418%l¬ŠÚíëååæõÿøÿ•tÿùÿ÷ÏÄËʹƒrx›ÂÜÌ¡ÎÿöÿI)ÿùÿ߯¶­dmd7A@?8/0/ ¶ÿúÿþÿÿþÿúÿ·)CE?@X‡n> Sm3)@Z9uW$.aŒ;A{=<9985544318(f¯·¾É¶Àóÿÿøÿ‹ÿøÿúñÝʹ®‚~›ÂĶžz·ÿ÷ÿ4:ÿùù­ ¾²ySUXJA9:8/0-¶ÿúÿþÿÿþÿúÿ·%AFAB(!d¥<=x@/T7‚^ #e‡˜V A<€>=<<9976443418(e­ÂÍÖÀ»ÁÔÿ÷ÿxÿøÿ望Ëï庆eWešÒÙ¼âÿúÿ-Iÿøû¬‡Š{_`fggaUK8/1*·ÿúÿþÿÿþÿúÿ·BHC;<9X( %1~L(>#‚ [6h.ze=<=>=}=<;9875443318(d¢¤ÂÙв¯îÿ÷ÿ˜‡ÿøÿüäÎÐÁ„‡s—ÇÍ­´õÿýþWÿùßzzqnlifbbXI4/1'·ÿúÿþÿÿþÿúÿ·@LIDC*;]}X <Œ[ :fNެ¥==<€={<;9776443318(_Š<‹´­­ÁÕâþùÿyÿøÿöÇ´±¦¦¨–˜²¶ ‚šéÿþô* ÿ몈‹woe]NB5-,0/2#·ÿúÿþÿÿþÿúÿ·=MKF&2Q&%lvŽD„aOWLb3]4?:={<:9875443317)\ž‹¤œ­·“ƒÒÿ÷ÿ„•ÿ÷ÿëÉÖÝàÇŸpg‡–›—£·Üùÿé×ÿ÷Ç¢˜†tcN>3..0321.2·ÿúÿþÿÿþÿúÿ·;ILP)Z2Jawp I(‰/Q1Y6cFL 6=‚cniBD^[' 6<;<<€;88875433417*X¡Š–¾ßÀ¡Þÿÿùÿ…¢ÿøÿåÂÌÅ•ilx‚’ ´ÐøÿÿニV0')/332„19001.2·ÿúÿþÿÿþÿúÿ·/LYR‚eqm|K#1km¸AA1nV5<ƒ;88654€3-17)Xu½ßÉ¢–Ùÿÿùÿq¢ÿøþÒŶ˜w}†—¬»ÑãÖ²yE)#)2542€1/.0€107.0·ÿúÿþÿÿþÿúÿ·*GUZtœp.3PpT}$>cmZ3<:;988654€3r17+T•ÁÍÙñàþÿùÿuŸÿúå¯¥š’ˆ„Œ«­¢†[9#$,5851011/.05820101130// ·ÿúÿþÿÿþÿúÿ·>Zi~†¡D JS 9tcAa 2<:€;::877€4v3326,M‹sŸº¼±±ÝÿÿùÿhAæÿÿУ›–•“‘}aG-%(0773//01/.1583' .2245750/-·ÿúÿþÿÿþÿúÿ· FR]\‰tŽŽ‹ +L;uQdR0<:;;€:87654€3|25-J€𬼵›ËüüÿùÄöþûì凜hM8+(,3651//01/.1682& 1UŸ§1:5694/0)·ÿúÿþÿÿþÿúÿ·DQ\Xql}ƒ„¦y4KC#€:–U/=9::;:997654€3|25/Bmm{’“†›×þþÿõðúüÿ⮇bE2*+03530//22/.1681&!2]šÙùÿÅ.:59;6.1'·ÿúÿþÿÿþÿúÿ·BQY[epz‰{‡®g*`?6OJ5L3{=S$,<9;::9987644€3r25/C^Qk˜»îÿúøüÿþÿæ¢g?,),25420/122/.1671&!4aŸÛÿþÿ÷ÿ³,=8<<5.2#·ÿúÿþÿÿþÿúÿ·;NWVmzwކ‰gžQh6fk=kI(=8€:9987644€3s24/Aizœ´Çâùþÿÿ÷Мb8&'.454100121..2670%"6e£Þÿþÿü÷ÿøÿ¨,A6<83.2·ÿúÿþÿÿþÿúÿ·:JTSk‘}•††_™ˆ‘‡faUf J&=7:987644€3240?€¢®Áàûÿò¾…O/"&1763€0Z221..277/$"8i§âÿþÿûøüÿþÿøÿ-B:?:3-2·ÿúÿþÿÿþÿúÿ· 5ORV\}‘–xˆg]x‡˜xHk  I:%=7:986544‚3(1<‘¹ËÕŤpB("+4872.01221..277.##;m«æÿþÿúøýÿDýÿøÿ”.C:<:2-2·ÿúÿþÿÿþÿúÿ·1IPW]v|—†on†aƒ|¤‰BIw0X2P" =7:987544€3'24.AŸ”vQ1#%.6951//1321./377.#$=q±êÿþÿúøýÿGþÿÿþÿúÿz3D<;;2.2·ÿúÿþÿÿþÿúÿ·-DPT[oo‚Œ‘zˆfwŠ™qpzsF”L>'<7:98654‚3#418?+'*49730/12330./386-"%@u´íÿþÿúøýÿþ„ÿBúÿl5C;@<1.0·ÿúÿþÿÿþÿúÿ·'CMOZlqt~~‘„g|€¢e5.1'·ÿúÿþÿÿþÿúÿ·;GPRDWEJQ~qxzšzt`Šn§1‚e> ;7987544‚3€201684)!+OŠÈùÿÿþøúÿþþ’ÿBüÿ×=@<9;4-2"·ÿúÿþÿÿþÿúÿ·9LQS&3J:9ƒhzoxŽ…nƒ :¨8gz*:7‚875543€2374)!.SÌúÿÿþøúÿþþ“ÿEþÿúÿÆ5?99;3-2·ÿúÿþÿÿþÿúÿ· 6MS_1:#E[lr~vi‰{¥?‡PTR½/;7987554€32 0%-T”Ðüÿÿþøúÿþþ–ÿEþÿúÿÁ3@9;92-2·ÿúÿþÿÿþÿúÿ·0N\f? E~pxu^koyKXe;¨ŠcFA4:8654‚3 2216„±ëÿþþ÷ûÿþþ™ÿ þÿùÿ«-A6:92-2·ÿúÿþÿÿþÿùÿ¶ )Kahl!2&2u_YFxmwy„ŒZ‡ZZ@ž$”ˆ,=68755‚314-HîçðûúÿþþœÿFþÿøÿŸ,B7:71.2¶ÿùÿþÿÿþÿúÿº Tzvyt ;D96ŠP{j…Ÿx„j[C¦-€‚"@59€8655‚3 14-HâáôÿþÿþþŸÿþÿùÿŽ,?5980./ºÿúÿþƒÿ.úíîîïëîU.[jnqj38 …LYd˜W„¢p›fWQ?pƒ'>69887654‚314,Jñìöÿþ¢ÿþÿöÿv,?6;7,7ÿæñííúŒÿ)üÿR(6GcgiiRGKxq¦zW]mƒ†fL‹Npy,;78877654‚314,Híòøÿþ¡ÿüýÿýúf5<586*8†ÿöÿþ‰ÿ.þýþþÿùÿH .4H^\`f=?w¡aaFs{]z”ih‡;6‚7654‚314.Dôÿþžÿýýÿÿü༔tf_[[SG7):€ÿöÿþŽÿ#úÿI(633GZ\\aO 0P6Wx0uuO†t~ˆt|H396€76€4314-Eõÿþ›ÿþü€ÿñÔ²›Ž~si^TD6-0*; |ÿõÿþŽÿ#úÿI #731DZYZ_N6†WjžR—…fy–~J396€7654‚313.Cóÿþ™ÿýýÿÿýèÆ²¢”‚ylZC3-/11);|ÿõÿþŽÿ)úÿI731AX\[[R mK‚jU£fhx[/:67766543213.@ñÿþ—ÿüþÿÿöÙÀ°­«¢—ŽcB1,/10/1*;|ÿõÿþŽÿ$úÿI622?ZZX]RTneTš^b}Y[/958€654‚313.Bòÿþ”ÿýü€ÿì͹°³´®¡™…a@-,010€/1*6~ÿõÿþŽÿ$úÿI632?[Z^[U%:`Kh¥|gT‡Rg/957€64‚3213/Aòÿþ’ÿýýÿÿúß²³µ±­²¦Ša8+-120/ 0..+€ÿöÿþŽÿ(úÿI632Z <¾ªrE‰De/9576654‚3213/>îÿþÿþüþÿÿñоµ®º´­µ§V1).32//0/ -0&€ÿöÿþŽÿ#úÿI631;XZ[Y_-3*)K„2›0_175€655‚3€216Ûÿüÿýýÿÿüådz¶¼»´´¶§zF,(031…/ -0+ÿöÿþŽÿ#úÿI6328U]^[\DM‰¥-T085€6543217Éôü‹ÿýþÿÿöؽ®¶Ã»´¸·¡k<)*230./00/ ,,/$ €ÿöÿþŽÿúÿI€5337Va][\NL¡5=475665543217¸áøÿþ†ÿýýÿÿýìθ¹ºº¾µ·³’`3&-32//01€/0/-.00 ÿöÿþŽÿúÿI6326T`_^_K,ŠL185665543‚24¥Ïõÿþ„ÿýþÿÿùàÆ¸¸¼¹µ¸¹¬…L,&/41./00€/0.--1,  €ÿöÿþŽÿúÿI‚4334Sc^__Q &†/95€654€3‚213›Âòÿþÿ%þýÿÿþðÓ¿¼¿Â¼³¶»£tA()240./00//00.-/1, €ÿöÿþŽÿúÿIƒ4€3Rc\^[Ua=2€655432 13‰²îÿþÿÿýþÿÿûåϸ¹Åû¼À¹™a6&,33/.0€/.-.1) €ÿöÿþŽÿúÿI„4332Ne]\YU&84654€3‚2 13{¥ìÿýýþÿÿõáźÄÃÁ¼Á¶‡S0&/42..11/ 0-,/0* €ÿöÿþŽÿúÿI…4342O`^_XW.74‚543€2!112m‘ãÿýÿýìØÓÓÌÉù¿Âª}E)(140./11//€0 ..12'ƒ €ÿöÿþŽÿúÿI†3341Jb`_VW6 6ƒ543€2€1ÞÿöàÈÃÐÙÑÅÂȽœc8%+340.0100€/+))++! Š ~ÿöÿþŽÿúÿI‡3240F`\^ZW> 6ƒ54€3ƒ20TV·Ý»ÀÆÑÌÄÊË·’R0&/52..010€/ 0/.5:7-  ‡  „ÿöÿþŽÿúÿIˆ 3340Eb]`[ZC26‚543€2120LQœ½ÄÍÅÅÊÅ·€D))240./1100€/.-..  yÿõÿþŽÿúÿI‰ 3441Cb__\[G36‚532104@lÅÀÂÁÃĤh8&+33/.01€0//0.--1+#|€€Š€~„t¿ÿúÿþŽÿúÿIŠ 1330?b^b^V845432105=ˆº¶Æ»”Y0'/52..01€0€/ .-/0+ôÿþ¨ÿúÿI‹  1331=`ca]93€544€3‚202.N­´¬G*(241./11€0/00.-.1)æøô÷öõû’ÿúÿIŒ/331:^a`=254554433‚2/4*u£g<&+44/.010/0/.-.0*ïÿþ¨ÿúÿI.331:\d=2545€43321 J3&/52/.01€0//00--00'íÿýÿ‘þ“ÿúÿIŽ .4325\@05343321+151//110 /00--0.%€îÿþ¨ÿúÿI -4327;2543€2131./10 //0/--0. €‚îÿþ¨ÿúÿI-5231ƒ4€3€2‚1001‚0€/,-0.! €„îÿþ¨ÿúÿI‘ +5253344‚3€2‚1‚0 //0.-.0- €†îÿþ¨ÿúÿI’ *6143343€2‚1‚0 /0.-/0,ˆîÿþ¨ÿúÿI“(614‚3‚2110€/ --.0(Šîÿþ¨ÿúÿI”&614322‚10 /--/0(Œîÿþ¨ÿúÿI• &61433221‚0 --/0%Žîÿþ¨ÿúÿI”#61€32210/--10%Œîÿþ¨ÿúÿH˜3/..-..--++-/, —îÿþ¨ÿúÿL ˜3765€4€5 ."îÿþ¨ÿúÿIœ  #›îÿþ¨ÿýÿ˵º·œ¸ ¹¶³¶·½Ãº¶²´¸¹¹š¸·½ú ÿt8mk@R-?êýÿ|Òÿöüÿ>*óûÿöÿu åÿÿøÿWpÿ÷ÿ¾gÿ¶ :ÿ– Jÿ¨Mÿ¬Yÿ· ^ÿ¿ ƒÿÔ0S‘¾Ýöýÿÿúìέt6  .˜ëÿþÿÿÿþÿþþÿÿÿÿþÆc    €÷ÿÿù÷úüÿÿÿÿÿþûøöþýÿÍvdN<&  (5@KVao}‹™¥°¼ÄÝÿû÷þÿÿÿÿÿÿÿÿÿÿÿÿÿÿúøÿÿÿÿýøëׯ›„kVD/  &3?JU`n{Š˜¤¯¹ÅÑÞéóùûþÿÿÿÿÿÿÿÿÿÿÿÿøþÿÿÿþþþÿÿÿÿÿÿþþþÿÿÿûöøùüþÿÿÿÿÿÿÿÿþûòáʶ¤Žv_L8# J‰¨ÁÜéòùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüûúø÷ööö÷øùúúýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿÿÿÿÿþüúù÷ööùúþÿÿÿÿÿÿÿÿÿýöèÔ¾¬—gSA+ Žãþÿÿÿÿÿÿÿýûúøøööö÷øøúûûüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþÿÿÿÿÿÿÿÿÿÿÿÿþýûùø÷öøùüÿÿÿÿÿÿÿÿÿþúïÝÆ´›mI+Õÿþûöùúüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿýûúù÷ööøûÿÿÿÿÿÿþÿí´^  Ùÿôþÿÿÿÿÿÿÿÿÿÿÿþþþþþþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿþýúú÷öúÿÿÿÝ;rÿõÿþÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþþÿÿÿÿÿÿÿÿÿþùùÿð7»ÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþÿþÿøÿÔ Ûÿûÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýÿöÿp!öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿµ&øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿüÿÚóÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø#ïÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø( âÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöÉÿûÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿò´ÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿéŸÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿûÿÓ †ÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿ¼lÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿªUÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿ”Aþûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿöÿ{ )ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿbìÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿùÿNÖÿüÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüý;¿ÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø$«ÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿé •ÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿûÿÓ zÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿ½`ÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿªLÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿ• 6üüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿöÿ{ õÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿb ãÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿùÿNËÿûÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüý;¶ÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷#¡ÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿé ˆÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿûÿÓmÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿ½VÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿªCÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿ“ +úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿöÿ{ íÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿcØÿüÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿùÿNÁÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüý;¬ÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷#–ÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿé |ÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿûÿÓbÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿ¼Mÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿ©8üüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿ”  õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿöÿ{  äÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿbÍÿûÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿùÿN·ÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüü:¢ÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷# Šÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿé oÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿûÿÓXÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿ¼Cÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿ©-úþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿ” ïÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿöÿ{ Úÿüÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿbÂÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿùÿN®ÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüü:˜ÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷# ~ÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿécÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿûÿÓOÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúÿ½9üüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿª"÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿ”  äÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿöÿ{ Àÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ÷ÿcšÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿùÿN`ÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüü:åÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷#œÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿé5÷úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿûÿÍhÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿøÿ¨}ÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿöÿ ƒÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿF}ÿöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÕ  yÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿôÿ|  uÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿ÷ÿÊ oÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿþöÿÕ iÿùÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿûùýÿÉ dÿùÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿúøÿÿæo ]ýùþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿùùþÿÛq WüùþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿøûÿÿÐd  PùúýÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿøüÿþÅU Jöûýÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿþ÷üÿü¸HEóûýÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿý÷ýÿøª;@ðüüÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿý÷þÿó09íýûÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿüøþÿì%5éþûÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿúøþÿä/æÿûÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿùùþÿÛr*ãÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿøûÿÿÑc &àÿúÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿøüÿþÅV"Ýÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿþ÷üÿü¹H Ùÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿý÷ýÿø«;Õÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿý÷þÿó0Ñÿùÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿüøþÿí$Ìÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿúøþÿä‚ÇÿøÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿùùþÿÛrÁÿ÷ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿøúÿÿÑd  ¼ÿ÷ÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿøüÿþÅV ¶ÿ÷ÿþþÿÿÿÿÿÿÿÿÿþþÿÿþ÷üÿü¹H¯ÿõÿþþþÿÿÿÿþþÿÿÿý÷ýÿø¬<©ÿöÿÿÿÿÿÿÿÿÿÿû÷þÿó/’ÿüùøüþþüúöûÿÿì&bîÿÿÿÿÿÿÿÿþÐzp·ÝôîÖ·•C  ic08fÛ‰PNG  IHDR\r¨fðiCCPICC Profile8UÝoÛT?‰o\¤? ±Ž‹¯US[¹­ÆI“¥éB¹ÍØ*¤ÉunS×6¶ÓmUŸöo ø€²xB Äö²í´ISAÕ$¤=tÚ@h“ö‚ªp®¯S»]Ƹ‘¯9çw>ïÑ5@ÇWšã˜I`Þò]5Ÿ‘Ÿ˜–;V! ÏA'ô@§¦{Nº\..Æ…GÖÃ_!ÁÞ7ÚëÿsuV©§$žBlW=}ñi€”©;® ÞFùð)ßAÜñ<â.&ˆXax–ã,Ã38Sê(b–‹¤×µ*â%Äý31ùl ó‚µ#O-êºÌzQvíšaÒXºOPÿÏ5o6Zñzñéòæ&â»Õ^wÇÔ®k¹IÄ/#¾æø&ñ½Æ\%x/@ò™š{¤ÂùÉ7ëSï Þ‰¸jø…©P¾hÍ”&¸mryÎ>ª†œkº7Š=ƒߪÓB‘ç#@•fs¬_ˆ{ë±Ð¿0î-LæZ~ë£%îGpßÓÆËˆ{èÚêÏYX¦f^åþ…+Ž_sÖ-³Tä>‰D½ Æ@îקƸ-9àã!r[2]3ŽBþ’c³ˆ¹‘ónC­„œš›Ës?ä>µ*¡ÏÔ®ª–e½D|Ž%4 `à î:X°2¨‡ ¾pQSL”PÔR”‰§aeíyå€ÃqĘ ¬×™5FiÏáî„›t…ìÇç )’Cd˜Œ€LÞ$o‘Ã$‹ÒrpÓ¶‹ÏbÝÙôó>4Ð+ãƒÌ¹žF_ï¬{ÒЯô÷kû‘œi+ŸxÀô˜ñú¯.ý°+ò±B.¼{³ëêL<©¿©Û©õÔ î«©µˆ‘ú=µ†¿UHcnfÑ<>F‡Ë ^Ãe||ÐpÿyvŒ·%bÍ:×iX'襇%8ÛI•ß”?•å å¼rw[—ÛvIøTøVøQøNø^ødá’pYøI¸"|#\ŒÕãçcóìƒz[Õ2M»^S0¥Œ´[zIÊJ/H¯HÅÈŸÔ- IcÒÔìÞ<·x¼x-œÀ½ÕÕö±8¯‚ZNxA‡-8³mþCkÒK†HaÛÔ³Yn1Äœ˜Ó ‹{ÅqHg¸•Ÿ¸u#¸ç¶Lþ˜ hŒ¯s ˜:6«Ìz!Ðy@}zÚgí¨íœqÙº/ïS”×å4~ª¨\°ôÁ~Y3M9Py²K=ê.Ðê °ï ¿¢¨Á÷-±óz$óß8ôÞY7"Ùtàk ûµHÖ‡wⳟ\8 7Ü…ðÎO$~ðjû÷ñ]¼›n5›ð¾êø`ããfóŸåfsãKô¿pÉüYqxµá†@IDATxì½ ­ÉU˜™w­½Þ¾w¿Þµ%ºµ" $$ahq †08< 18ÃŒƒG„‡AÁŽÀÆfr01x iXHjÉÚ÷¥÷íí{½Úî­»Ì÷üóÖ­×ï½®'uKÝMeÕ½¹<™yòœ“'—ÿ¿µáp˜žÊ S«ÕR·ÛM«««©^¯§Í”{*¼[ùÏ j í¥aš«7'Ö:µ¥A¯wëäl2}@úÃå´«ÑnMÔë½Î`0l2¶OÍÏL[ŸI¬òìÌÌLðî3YÏswój/‚¿²²’¦§§Ó»Þõ®ô‹¿ø‹iß¾}iaa!µZ­-Ep5~òŠðÏÔšõS‹ém»oê¿eÇ¡ïÛ·óð÷Î7Ú“Aï‰ÿpâþÿðÇ¿öÀ™µÕæd£Ù[Rõð|Pò¬T¯×KÛ¶mK÷Ýw_š >U!l¹¸ªØšÂ0íâÅ‹I¥ 5°e \J¥gE¼Þ«ÕÿãÞÛü£ý/úíùöÔ[zË«Ã~êÔ&kµáÏo»éþ¯¦üò<ð‘wýù‰f»Vï-¡çƒPÐåËF£±ÅŸOÁŽ×¤ ®¢I‹_Ò·üo¼‡éÛõF­;èÞ¸ýà®_½ýÕï[é,ݽÔ[íL6Êö ŸR«;XkLOLüúû^ôÆ=oüòûÿÑ_^8Ñ¢ÜåjuÄsY «µøß¾ynÔü )€2ëë—ðs£»ÏßV"´¸C„¸µ½Ñêþîͯú••ÕÅ»›©¶Øª5gûƒ¾r6ðt½™׺«³­Éÿá¿?øÂ÷£>ÐZä¯ †Ãç¼!Px²øÏßQÿæ{VÿæQlaøvS ¤áŸbö§-ÝŸÙwÛŽÝÓïiÕ3Ýļ¯z`vÇjC ÒtƒýìÏìúÉùf;õ‡CVΦow‡¶êÿ–Q`K|ËHýLVTv·j kÁœ¿9Õêû˜ÍÕµJ°•îò!8lö8ØÝhÞó3;-×md4aßr(°¥žƒ\VíYv׃Yœ±-Ëù’Ÿ;ëQ`ÕþrìLlÿ®¹½šÿê"»-K "ÄóÞ+#þ¼ïèó»ƒE`kÎø©ÙhÁ[©¥lä‰ù­ ûƒmvº°zñÈoûê*¹õ•A/€ØÙYÞ =¯)°¥žÛgúž‹zù¯/žz`Ø_û\5£¯U'ü¤º‘êkîtý{YÿK…vEŠ¢Mªè–÷|¦À–xŒn%Ùµ.ݬ՛~þhúË…ïšh¶Òù^§×àN0 6Þã¿Þ¹ÐïN7&fNþÖÉ~{‘™Ÿ Ä®x¶¤ÿyÀ×Ð…-p Äz6ƒ"¸•vigó{â‹ÄÁß?žŸ˜žò´6¡ð9h¤åabÇäÜâ{N>ðSÿì‰/>Â&as…»”«©!žÍýÜjÛÓK-ðôÒóÛ†­ÌÞ=¤œ»ý½¿X8Ñxí—ÿüWï={ä̓Úà4Õdo õkýNšÖþüO=ôÊ·ýÃïCæl ºø¯,„o[¶*þ6P`K|ˆþLUY¦n”@ ³¿ÿÉ‹§æ^wÿ‡ß÷»‡†¿Õ|ÕÍéì¾ÔYêK?ýð‡ùí÷}øË“õú$Âï¾Á–ð?Sƒò,Çû Ý|–÷éo|ó”f7øØ\S)<ÚnÌ~af"uç&‡÷mk§¯œèï’H<1¨ðoI¿Døê¶Àópà‹%°:`ß³ðï÷[GYÚé­ Î6ëi¦Þ˜´ÛÀmY€ÏÃñ¿–.m1ÀµPë9ëÆà'âŽÎÎLÁ"8߬âÂïCi0oWbúÇ/ ã9Ö½­æ> ØROŸµ(¸ÚË»@ÒÚÒÒD»QOkµúpE°szf*ÚœüyÖ6«aÏ<¶À3Oão[ < 7l1ãïjµ§/®õÒYî M0ÝÏÍÎÎðÖ Ÿät»`Ëý ¦À–x>Oþ =dþŸ\žh§‹Ìþ§Ù÷[LCÞ“µeú?‡~Ó]ÛR›&Õsá×ñTàÄa_•µÂ²`7O7sÖÖòÿ98¬Og“·ÀÓIÍg®òVa¡g0³Â^@›8àuÞ•éã¸òqŽm}ÿ£ÀÖ1às|ȱäc?6•G°ŠsÁwêÃ!ok¤»$XFèãâéÁ1®ø"ˆ ÍÄVðyE-ðÜΑŒ!%Ðf}nÖ<ækòpY3¾ÒÝ‚‰Óæýù: ¡1‹ãÀ!ŠW,‡qÉG-Ì–{Q`KtxjçÄÄÞÇ{k{y5ÐÎùÁpϰÛIµÁ ÙfO`w£qÓËü^„±^o=Þï>uòÄ*›…"ï].Kð«ÚX=ò|ahQÚôì'áV ¯D-p%Ê<;Ò´:Öð¼¯{ØfR^ð´3?½pï¾Ý¯=pðÕ¦gî>Ûïž®Õn»urúFÞõ·÷üp0¹ùDZ3üËrª÷ú핺Ïìø[³Û÷ü%ù> °rpìðžKÃvöÓ£÷.žÿBíü…þ§S'O^ôW|OXXÔÚl(v»ý¾–nÔ¾Ýú~®Q`K<{G,„«‰ä;Û³™ÇÞ°³gçÎÖ¡}û_ñú_ÏŒþšáôô+ç;ËéîZ+íV2—W˜Å‡éEµÆpvXëO³L¸³Ó«õÑ%¼ ,ãÀÁZ·¢ßo¯ §§ÒððÁFëð\½öš ­Azíôþ´¸ÿÀ…C7Üð‰G{k9{æÌ_=~þÜÇNž;·Úeé@üªVKKÈW,ƒK’#ZéŒËem¥}»(°¥¾5”¿š`\Ú‚/ñði!ü}~‚­³ÿàÁ]wìØù#7<ð_r‹ç%g;<×ïÞâELÿFoëŸg{ÓÏ÷Ï¡4öy؇߫զûýÔfÞ&žfØ ìðÚ€/hbøû9>¸L]€>Öë5ÏÛæšÍïÛ×l|ß¡ë¯ÿŸî>|øÓ_>uòÿܽ°ô‡9yüì…nÇiÓXöY3dw-B~5š\ žKé·¿ l)€«ëjLziÉ«1íÕò.ÅãOZ5ÅF³Õê¾ðÎ;÷˜Ÿÿ‡»wîü©öZoï"‚Ïúœ¿zw ³|nb¢5×4y«gs©>L»Øôˆ¯ƒqÁ}âÛùØv ݈…½¯ ŠßB,\¬§ú" Ç·ƒvxkPwô`Ðc ·Ð[›`ªÙ ;w¾l~Ͼò†ûßý‰Ç~óäÑ£®õz…F(¸gà&¢‚qÌhÕþ*‰ø ûÑ?Ç®ü]ð_b³8.Wv+ <_ÀÕ˜ær%FºRúåplHCJŠ0( £4™9]×GШýþpzf¶ûò{îþÑ[ü¥ÅŃk+«Ã%ÞÝ?ÉÁ/ïkMs¶ïûþ¿|f´îÙá¿ÈÄ?9Ôü>Mð_Ç©Cá×bèà/“Ö£ìE*ì°)Ø¥êóÔ½L|™0ÊÁ× ×Èk5xœ€M‹,Î÷zÃ…Nwÿt£ù_zø†Ÿ>½ÿ?þÊ×ïûw.°ÃXcÕ‘-ûTõ ŒOrÒ@ZÈ{5–Æ£ïø.)(ŠY²ŽÀ¼Í:qmpø’ßô<Ь³Ýæ:õlSOÀ§èÆ•˜ãJéO.$´ÆV{‘Þ¬–ÛÀ£Á¶£&pù3Ëp9Ë݉à¥ëå*;{ÕÀ)Ã[n½õºÝvûoN·šçÔ… vểv«É1^Û]þYÐ)m˜ëišv xùçì`Xc/„Þ€V™é§P¾p7½ág\Óv>+tc•ò „—/‘w¹U‚‘Ž€ó!Ë [ „Y:p¯ µæÁÛã çyåð ³¶½ÝhüÖwÝyç;ö{giÎ)öŽ£¼N®u»§/^{޲çaà[¥.GðËúri›%;cNxëÿÈHº<ãÊ6볎uÆÌƒŸÓ1UÇrUÁUn¢¢ŠÑÀá¹|»Ùnµ¦¦¦˜°[|šœÆ¥9„{Øm”™ÃŸ›˜œœã÷ëghõ\­Ñ˜ÁDÞ6=99uËõ׿teuõàÂÚZ§59Ù¶= ¬±/Ðäwýþ§–%g»/0¡Öñ;À)ÄûéÈ>œ¤#¤³Ö;³ß‡‚ÐZCR Ó“ @â"ÿ² «Wø+~ÊÀU#(>?Ošu+¸ËÖ³!ÑA†`pKTJžÊ¡595Õæ3Ùà}yä)¸³0΄ڦ bVϲ6Ÿ›™cfža¹>£œLÕóÜ6„rV8ͦÙÁŸ¢¬WðÛ¤ûüMÜÅ·.–רÁˆN4 £4ÒÎ;Swµ3`ví¢œŒ…®ÑNÚ7ü°ec-gsO§5pö_,Û9\Fn&)|x·oåkˆˆJ!^Œ£¹oi÷ lO>p}ö¼)ØBaøªqÕ!/¯­â³GoæMCõ}C²†½µa·^ë¹—ÄÎ ¯&öýü€À®R=?R:?ÙlÍ[­C ˜· øu´—þv‚³¤°@vV;kƒÅ:VhOõP Üu8ÉMÆÓX§hËI,ŠcúÕÕó,\ÉxTéÝ'ôÛºU¡u1599 Æèj±,L ‰ñMÄ‹YbÞf] ¿ðµà¹Šg&ùéV……¨2il4Ë lZÉð8¿°ˆÎŒM9@ÀìäâZ­==;;9Ñja)×f̺!ˆ.“âZÚêLŸÞ®÷=dÏ öXB 4h˜ø,Î9†«CxHù(Gù.w ½$Äò¢Ö!l~ó\䂱Ífl›\J‚Ì‘¿4by`]ô»°çÙ^RDXZPß°ÛëósF’cF!«6œbL¦ê͉=ÀÜ¡b‡}?¸o_ÐR|´s Ű²Ú믧—ÇhÛɵåÕ3ËK‹§°žŽÿëßùcgX’`P­iUx©ÉeÅé tú>.•·%Œº}£z*ïq”.—»Ç·ÁM¹\ ¥ÞªÔfËo¨Dö+';w.ýÐýPúžïùžD_ðY—Ô³¡üÓ­b£¡¯úý6•ó«Ô}x?„=*o T•ïÛóÛ¶M8è%Væ°˜Ëóäsº¥P6ggæç8¦fvExaÂÙ:ÂÌpm§wsPŽ®µév³5EÞ$³t ¡m!¼1´R¼P==H]Ñ»7GÚóMF7ÇI†Q•€°ó;Ú¼i'VÌl¢É›¹¬³¿ÍeÖLóëÎÌa®#drSCD2µŠD¶N•âÔŽifÛ¶S0A‹!Œ3>ëg X`Ú‹ÁM=˜.± Ÿ¤³¶Ž¸µ·\[!~al/{  ͵¿Œ£²Å…/¢ÿøC¤Ï ë[…0abËσ*U€lSQd5n³'aU"YÖ–Ukˆ–—v*„–íQr£ !G¹P—™X$4¤–âÿM°ÝÔjQÊ…†-¶FæÁu3+€Ñúi°ƒeÕâ·þÕ¿‚{«(‡…N¯–èT¿×?Î^ÊɥťS]–ÐÈ}ÔãT¤Ä~hèFõº“”Ÿ«9ŠÆ¦¦,$+U|ì¢á¢4F\ÿJÎòÅYöªNwŒ§§§ÓòòršÝ ®Z˜Ì§CD‡exÂcçÃÌ(Ñ:­v{¸ÿÀ…ôåzfçç¶o?èuǘÐ{P·ÓÌvšÊðØú˜×V!)Æ`‚HªÊh–!Mƒ4Y|2uÛÍ/Êó ¯©„rØM³a—fVB‚ Ã`¿†‰€8gæ5¦aíXwÈÕ»˜©sãbh`ZÒÒ5¾|Ƕ §à8úºEÂ"uJW¸±í4n[ºhïí3¼¤‡òSssö»†B¬¹žc‰ÁŒÊ<‹/–¶|gÁ&ÀÅjff– ÄÝç¡›P ±TÀyü‡™mE €ÁgW.•5Û®Àñ…wŽV±ˆCe¦ðºlÎr¶WE\íB4Uõ8]§Ú_GCk2ûçô6ŠAûÇ¥€ Z"“´!gŸ*‰›Æ‘g”w¯òà?êtGUØ¢¾:†rü£°A˸×& ãõ‡aüÁcµÚ4*k¿öh´Ò R»ö‹>’ÕéözK°ïyú†¾믭\]]=µ²²â†æiê;ŽÅ{vñÂ…j•”Æ (C©Ê«×ñSO($ºBŠŠÂO©_%'˨ô¤¢ñàw;Ji£º R®[zp-ë±|³ ÀÆÙ0&E/š W÷îߟfçæa’¼q÷¾}?¿ Ýt-ÌÔq(D·0Ú9Å,ç@ž±‚5^²#Lœ,ËAH7¡®/5e©®PYX66ÉL„`V péÅX º¡2"Ž@04E²ª¡Y çT#fJÕÆRV÷R>pÖo9(­’g¶!f?³iÛ¼»Ï cªµSa'9¡4ÓŸ6JÁŽ6 ó œä+Â9{ÛÛcžý3Oa–ƒrä §9tƼ&*Ð4 °(8€åéÀˆ c¹âÌG«‡ÐË0öÙ¶élƒ}g½BÍãâNz½d.eD¬ÌÙ´]Ã!”Ÿ–ÔÄ™tè€-̨~{•|y!›ŸACÕà1ÞQ'Å”uðÈŽ÷ñ·}¶»Ë¬èNbàÃ'¿— °$ýrØùP—úe¢ÍƒSf'hovÌå‰IfW–—Ñ?Ç …ƒÚÁJoïÞè|š»¡yŸDQœ^^Z:NO>T§;++V»]÷]e%›Xô"Á Î*«î‡ŽN2g·t£4yÁ~8ûë×kq…æ×R¦ÀÚ^:ÛhÓùîŽ;gï¸ë®ŸDðßÊfÖ«»î¼BÝåâŠp|ÜêCX¹Ì²LiÈ©™ZËv„Á a%O*e 8H™‡Á‹xIN…Š ®Ñù]…e?P±4=ÛÖ¡PZ>f>|ro¢¬õ¨Š£m5Ék;,ëǰ0 ¾m7_3TŸy Òí­&±m÷Š®õiRghtúÌ™°¸þ“Ö0égTSS±‰åì;*”Î&j|f¦vqÁÉ1ðZ ÂôÈ³ï ½°m6Â\*ôCP¶ÒÁöô»Öt­’P̪*…â&¡ûºå‚®PiÈPÚžàØE%r˜Ü΋©8úb¤¯“”r)"­BùÙpœôëŠ3ìi–-'’*À)£#œA׊Gd¦ !íQ°£€…b_’c‚F û‚EKžA‹˜TÚŽ‘ã´Âw¼C+àaÁ©§“Ý0ÈmÈ ,påÛæl{¦=‹²9Hÿ_d¿,/= †…2°òè/ö×z穈%ưZjÔN-\8šÌcÜ«þ G9rd‰1sþ*‚ º:Ý!WÙTI‘f!O¦Oκ¶ïkRëõçJ¨<„ßÁƒß}û ^ðÛ­Fó®N·3äÇvl3Z‹V»{e]ÜȇÞ;^9V©J;/<Èí @³b@3n8g÷2¾öËrÒS'™g_u¦û‘vö×&Yg/Àc¼œzbbÀ©9çÕDá-gyÓ ãÄå0„S×HqJ°®ÜÆlEjä3›u’Qgh÷`±8qCy²Ñjîŭ⩚ö`-Û7êàØ¥~|Çž=÷3~‹âþ3§N}â¹sŸ½pþ|Ȳçì5I¾Oi«[D5Bw©l’·)wM ÀÆŽ9d¦Ö½ý®»~îðM7½‹Ù‚û"Ãn†Å±Ÿ3˜Ð…Ó£™¹Ó´Ò0ÞáÄGxÖÒ£ª&<„:£(¼“™ 18øEðEjšèCkã>+!¬)©/SÈL Ìiy…¾˜èE‹DX+7/3rÆ3b•æšØ«TòêÅBší( C4¶DÆqf”KlcÚgy/çX/;(éÏýL®®&¾I\ˆú—m'ew³Á6E*6mGô(ÇjÚ…¸@½ûIwWÔÝ}qO“®À3ˤEÊ{G`%Jú½dp8Ö‰Û<é,-‡J`†4÷/̳ϻ𵕅áyBÞYâÖ=.SÅêÆâ6ü.íV¸¥2(¢¼i_íwX ÁÛYÀƒQ޶‚ÃÍ=éªR xÒhB„XÃ:ûziÜtóËÜ5ǽ(ŠøÙ$Ú ÂNVO+‚}m£øED¾ ´'øÖ- M!pDZ´û?¶·%>]òmrlQ8”ŒëÚ*¬Õ¨ËÕ›ÍCàyG°<1ØwàÀ .|àÔ‰µpþüŸ±7±$¯â´ ~ù˜MýœôÞ´³:ü:—XÚ/Õ«þÍܶùŸ¢a]6ì3 ½Drr5´#”`äÕ¦®¾æA1=(— fP§€fg(L†)&;X†£¾æ@•²ë%ó É¸Å‰×Xä<¸â‹™Ç<|[Fg()|Ó—™‡`†¡Œø¼b«‘%ÏvZ æ‰ßšU:wÞ‹až8…ðÅΜ>Ù7A|¡ÝAN!ÞFåœ „Ù¯r‹º:츓ž·ë,=P(ÐKEÆóiË MÒœ٧ƒÐ›_ b~âÚ a°|¼!ˆPÓpë?Èø]'yÒ.æ2ljÞ{'À-òõØFëufuYãÑ E¹\Êû[ã®J§Éw|T€EÛTNÒÓYX¥¦€)~ty|œPÀ8Z ',>•yà%]\EqHÛ!ÏT{¡´¤³ËŸwPrÄ2æQ$òôi›Â9MsÀ|]ð1~Q‘Tr³5Æ-ø€±9 Á™3^«Ûš‘C¶cÃ:ªZ]YñîÈíóóó·ÍÍÏÿ7ÄZY^þ],‚ß;wæÌc¬føUaû&ܵ(€Òy·{Ûw¾kÇ®]?EÖ&''™,XasÓÊÞÓ l"õ=Œ†Ì„“p£8Œ(W˜§ ÅaÐŽñ§v¨i'bS O´•rG$¿ll¡Œá@kθNò—Á7®v5®@*`”°emS¥Ò¢åÍR  ñš.s¬(HõŽ&p0,0ÎdÂÚ>…U·N…ËÛxu/±ßëºZ.ÎÕ™¹ÁÏ9A(yZ{,}è8¸aÆÉJÚ b÷;Ú”›À 5­ÅŒ>ÅXø À¬æ<ÄkÓ¦YÊi]8N®Óm‹7©5y'‘oÚ¬R–K}Û›wœ8mSØUÑúàú|ŽòãcPhð:—ÒˆæâgºH'…ÏÕ»ÖDQ¤*Y­áä%%ÂÂóÑYŸ> ¡Ú2_«ÅüÀù¹AjÎ F@•‘mÌÌ”ùÌò§^ßtËâëmpöÇaŠ‹0ݤÙáÂË‘Ržuš¾Òƒ,l‡>  a“½µ›‘³¶}çÎ_¸îðá?xøÁ‰%Â9&ÙPv¯jNÆ¿ÙïÍ*€@Ά„4tÃï…{öíû)Ö¢klNÍ8˘Á:†»/KŠã²5!ò IþHøÉV2 D‡ aâà‡ÂÉxF‹†H8>T[9(ëŸéc®Pv<ÕpÔ.]aÃöA”1ààÒ/#âlb ‹™®À+ì†uÑeò\:¸,ȸ*뀲í^%á¹wV:y“Lä _MJÁ·.7ÊlKÙ`ó ?îäÓ&Ž‘g4qîZc>rËËÙsš~ÂrÈmÒJà±ß´ ñ¦I“¤¹öœ˜B° |Q™Bn_¼ÜwéF]šã¦»¾Ö¢Ñ _8â. TÚ~ÂH‡`2*r‘ÿÄSŒÑ—"ØR¥ !Ur„–€4ÐD·|±œlN:¢Ùál3(«òy,¤‡pŽO¡§á(°}±-¦VÙ‰ÃcÍjf%qóÈ ‘¯ó;p]&l~†È¡°UæâDŽˆ‡P Ë8ãÛ– ÞEV|ÿ¢2Ä0¬ÿÌ­wÜq×_ûÚÛ±$yéê“d jÁ½Í*€@@;þ-·ßþÐHll´:^B¡AXŠ™2 xP’h„£_ùKµÄÁNªuÒªœŒ @àP8B¨ñ#,ŒNÜ¡WðrJ$Fù×b£{ ¢åLóSLáKkŠ<|Ðs¦Ìx¹Üx~†#Â2½D \ÔaMeÍȸ¡°¦‰+¾Ôã2ÎÂlVÐ}È~âC?Z·‘×ÝœQ1Ó¦t4•º„pZ7wlQÞ…èsý¥L™îaq‘Ї…ØÜ³¡6X/úÄLNšux…xé ÷cHµëÿhª`ØFMþ]àwÉàu:…ôþ<ñØ`£ì$ùeÖ.4à½AHš4´,i&gf«CÉpL´–L×I˲, X(§ÈƸ‚-}E&>•°ù  y5M¸KãòO¤“'D?C GÀD¹*¡J¬G;ÖS#ä—ÅÂÀ(IÆÄoáÈŒú4xÊžy Ïl7:ƒü°?ÛûößÞóØÃ¿åıcç‘Á@W‰aàßÌ×fò# Sc­Óérëoí?tð'Ο;ï%îr„@x= !qüÄåž|lÑT~bOÆóx0àHSèBÀI°éൌÖMTž;aG4òñK˜”¨?+ÉÉ€ZÐ\Å~mäE¾y™qõS˜Ï1f¶Ã> ùífV¤“_PXMçBÌq<Îrª&/´˜nL3Ö6¨,7Ešù¹†çµÝ˜½œ‘ÏëeÌv`áýuñXn°>Ý7>ýǽt½q>Þ/Xaf˜%vü»lº“¯@yw(çZ]ëfb)üçÄ…`XË-l6ý<à×…Á«2°ÝÒÛŸ¶-âäŒ (c9ÍöÖÙUšIÃB`ãÂHo“Ågi?¦Y.sDUŒLÓM³L ‹«”5ÝLš-kDzÖ˜Y|£U¸$“TízJ¨£:àóÁuÆ_Ò / —M €DU\¾]O3âfã¼­à&Nõ´°S\jB~V9bÿn6 ÿˆÂ7¡$'EíÔæ]áÙÍ”€g†+<óv*‡7+4æIÔhûbÃI äy´P983s­•h¥œ™Æâú´BÆ¥Yð«<AÀáÎùùÅ%½à S.LÛŠÐ/BàÒÕ˜ ˾I˜sÖQÑßö U†£XƉ \øôÉzU{f …G\ÒÀA _E†¥`øÂŠWfvg?+ ˜œ|ëufVЈì&í¬°7qiL‹ºö÷±_Єz I-q4]Ã’¡ŒýÒ ·íšôG˜ix8#¬rî¸a|ëp`Že|2Ð5¼v§®l9ÿH£5|]r„í³Iâ4bXšÈ­öYWz<,}-«+¨ù Ÿô(P(âÞ2—–„‘20!b_θ3i®Qº®»Ë¥™Kº„È®ƒ‚~Or—I ˜'¥Ó"ðVi#m|â#kA•8…‰O,èµq`%vV¶J†”áa+ ÁĪq‚VÂël·p9ñ’§’ðb&X´]pÂ2j˜eø!`ø1Ëâ[V&U –ß´““ôuÂø±*C¼®™­ó-ÒlöÙ|g]Ÿòsvuö–NÂï°lÂ$çñy 3x^>ˆÓõþ c²íLOð—¾ïðµa 4P>ŠB˜¤¼ÊÁeŠ»þçI·_ vyØÈþÄU]|@#Ï´hŠI¤É`Ö¨ÀÛ_óôí¿á'®Ú> ¥,œ4áŠ_Òõ=¥PÞ" ¤%/* ?ÇùeX÷†HF^}/%ÇÁrö~@cÔςIÁM#˜ñ¥Ø*äýpA FŸhæt% 6 çB)<ÎX­Ÿçyš?XZZú:r';HòŒ8c»â÷f@U­›ß@«ySÐñ!Å9K¡Uà+Å•A`) •,«BŠ‘g~™ÔXBã2ÁAW৬Dq ê@é<µ„²Àw¦+‚Ÿ}H§ çŽË/Ö®q¼66` }m«‰¦‚ÓÛ sÇ~Ac¸X –0?`ð3Žì—Ù<çe¥QÖºV!¬#¦€—.Q5_3„<¼Vì-:/Ÿ«ö!¤Þ§Øò*—$j–ö-£ä¸®J9hJÞI>¡JE«â"0g +˜ÞAðèÏwÏø]†<‚ï¬ OÒÝWØu€F‹ðߊr;ªög\gŠM<ãéâcG2#]ú–B„uñ]…#>Þ ôàG% ¬+ðçœø˜Qh< §àâ+§[MŽŽáƒ€/£­Œ¼h ?iº,_Uæ›H^gÀ¢]ž`gcþ;zôgÈ+ @è§t›QIÓ~ !¾Á{ßTÎ#Û*)è8¯„]ØHÓ|‹¬‚«`»ö§±V8tÓu¡•)cY{¥¥ ¼xŒ ¯ãQÝHSøcF"]B…à.p䇂 /0†iû†S‡ ~\) ,̧pQ;Åø.Ãd’aœe ûçw´Ôú ég”NX+CÁˆg|O„Ñ‚°OÎØ ¦”õ"Îø¹¹ö›Z3«l¦nÄ« ŠÇÚ³…’$/‚Ì Æ‹¤{VÏcq#aÕœwlƒu[Ú³|^‰Ù»R$aó‘gû‹°ÄI!ûá·±õpÆŒbN9/Ã.¼Š¦—›Í¿Rº´Ïås s…+#¯ò#ré×xȃ~ãn#=Âò¡»œ0¾^ÜÏÜZ·bü¹ñÃÜã%@õð¬Å”Öì¯eܯp@Ý(,­³¥ãÊÀNúLéAÕé ªwÝGûà‡ù*¤BO¡ìºù¶ÁñºEøXž4dÅ`¸¤W~Ú0Ã/ .’ècqZeùÄ‹;¬é–fðºB]0I¯P ¤é—tSsß@ =ÃwÏ@KÂÝx©ßõG8*¢½¤+°ç‰G>8tÖW±_ø€°¶Oe⮽"\–5e­•!Œð&åF‡€Ð&þq93ÇÍì*?G6¤Eó®*7ÂYÑNì¹UMÖXài¡,0ÖI¹åFGšŽ§F˜´‘+a’F#á “³7†ƒGÊ ‚ÈÉI—aá¯ÜSª±¦ÝdŠ%÷€°>¦š5S}H2 Û± V‘¯`ümøGàSpÖ&)Ûvr‘<â®åuÖ©2 ë ßTÙÓ&u+uc½Ž c)mÃ…—Ã9XÒ×ÓÆa‹0F®ÇÒÃ’b ”ÓôðÍ Q†$h-…b‚õQ8æ›gZ„ñÃBˆtúk¹ ƺK\…‘…Ú&[äs§ XÌ9Ë@Ýá/`iG >H¢Ÿ¦-âáÀÛ[wöcØÉ"fv×ïneªxz º$Æa¶“o ³i–—~yæ`Ð#(Jy•‚NQ\#‚¤V^´Ä4`…+ù–Í ÃpÎÚ˜8U 4hT¶T V ÙÖ\«<7–A´È„ªhŸ!2à“ÁÅQ¥Vð1ŽЏxŠÉŒ@)bM†…4¾ —ò9±šÑs»K}#œÐ%RÁGräŒåU‰ãåƒÒU}%½”Åɘǖ_PYѱ_hÇ€.Ü”(Mcóo…Ù_) óß—S(*,nÀ· ¤ÑtåL«ož ¤‡€ùpÁä+ˆ¦ W»4>Ê p( aFŠ4ó‹ð « XâþEÐM÷c½¦YND Ò‹ÏMD_¼aaß—§ožŸr&¾®€G¢,€Â¶É…aݘ‹ÙÂDáèƒ.÷_æGÉŠb•$¼†ÐX§0¾=Ç FkÊd!WqÄUfüP"Q¹@äÛJ—$BQ6üÜ sMÍí0ÚUÅ£8HTR‘^åG週.œUøU\©¼Š—œL³œ} fZZdcp$1ÒùÖ"M6¸¦Wyø*pN3ÿ#ø«`Ö•ƒU­‘\•+0â+é%ãcë0#ÐËÀ–6¡¬Àa£1×üZÍ/`\ ÄœPÁ\ÕÛ”(´Çü“)ZIÄE€x$ª:à–2ÿ!Ä@:Vμ*ŠÏb© ÍýÂ@†užæ®ËJ ƒ²Z ñ‚ ` »`[Âlæ¤ ã¹yØð4ÄòÀ•]ØîM‹>ð!¶û¡oZX •€GyýA¶ŒÇ²ü¨×:Æaó1Oß2û-á'¹M)€ E;8 msÞ]fGï VMu¶3}Èæ´Œä«Âjà&‹Ìi”UcÆ· gcg~•¨Z­ý}É…K…_a5KúËëQ2ÇÙ QáØÖÈǯƒËŽ<ãKqНôÍvèT(–, Œí³¼Ì¨r Ÿ´È·°JK)3¯Q¼î$_xÀª«®· ?ÔÈŽþ–^Ò'ÒõúW¥›á̬¥–¶ãiÆT†F_ú8Ž–6ʈÉ|gاÍÂäàHŒ^©²ª~Ó£ªyœ{8~ée`B¨ÃWª3>©B®š·¨‚Zå— ¸ÈXW#Pg|¹ gÜü«þ…48r^ÿ";z™óÇÒÇËd¦hSôÕRÑ\&ÉuIdaªäâGt~<\`«4èÞGN&yJ÷:²d æÌÀìå¼§R¥-áóšŒË3af°æÀTf)Ñ*"3Å:™1ËJP¡ìó(«Â ÓÉ  †° S(ÒŠÐI(GŠ*¹RkÀ`X&ð§?"$íÐ\oMøª-êå*µ×©…·._µ%ÃÇæ!áXž(è q%È1@ôÍ6•¶(ØÖáRÁô`ÚJ€4éažuˆÇrÂ[N˜ÈÇw¡^pIÜq:Úב£½¶¹ÐDA5®G?òU¨(6]É##Úð„GBK8Tþ8L.œËDxüKX}±½á WŸõ¤õ´ úºð+øP” ²'1þÉ7Ò­à4Í<3"ìw•²_žœêŸxNú©Å-)FŒV.â{”¨ÖóKúh=An·Ž°Š#½H/ÐQ©ñ*½ª`CùÜ× 9WŒŒð®C¸å¤ÓPÜ´{*ˆJe0°¯R­’*×KÇ‚Á¤‰1£C¤ûœ‚ã2ë$‚ù8ÀAA) nq_…í+¯<'ÁVÀ»Z „-SC<æ¼ñˆ­¸ã­¯!üÚCk¤@ ×ÉMÞ¬í ìqf~ S&@¹C?Ê£¡L§îxr›Æ¶ëV߸í 8âk¾€\Z:¸é¾áªŒåüóŸâ¸Ü÷x!¨ÂNÿâ•ÛX0öuüã C‹bÊ‚ËòâÅ¿xI£Îª"£ «Ì(±¡\p1;‹t¼ÍâµÞèGI | þ†~š~ÙϘ°0¶O?êŽÆWe+ZEkÐÈÖ–~Àšš3ˆ3N9"d¤GV¤I¬Ëycá€]/¹žC-–&ÑÖŒ2œ4ŠX‡.x#e¼®È¸†¯ñ²„ÅGý1ÓBgOyu—Tœ/ýÞ”(½Ãô^…©=™ÚÁ-3ªÖ¨x©T§-½  á(û&É?ÂØp>æ;sñ’#2‰Á–ñ#?ÃŒmö »yî^Çì¯`(x*”rW€òÙˆÄì¯ñj-o2Úß^ÄZ°Qè |)”w´%¾Ý8áþIÊÚkã \lZªôEzxù´ÁWpiùnD?¶-|…†q•EÁ¡p©<ÅbßxKL(-÷=0ïxO ?t11iZC.wJ{Åá>Kà”–Õ k›CHÁ0U}¶3(n[Œ˜OÂŽr¥lå›ÂLŸòI eGºxÅYÕp¤ˆN…I(ã$_Ç®÷¤ÿÖ㟈&Ló_†Ë0RÈþ˜c¬r™ø<–Yw%ðT_U@¢.ö[_5žVpºÅ¸_‚Ãv¼þéðK{ÄUÚ(¯fHx@I7ðns ×#íùÁ™ÁI*;DÇâ.@ÈJÜß´&˜,Ú/’$cÃìôÉ-ŽY9‚–9ì@!V Fò5\1«…@*ì€O8…<6IÆt(Œ* ™.„ñÉÄ JE ´²¼’ãàÒüwÆW ](H «‹³­ƒ+.Ë·øIÛë òó`6÷4t¹Yùض¨r²Î*okUyôxù&?S•xJÔkZ¹¸ä;U*…a ¾¨›_á÷¡üQâ‡M¿&G AëAÐÝgX£Vi‰ßÖa)ƒâ!ÍC-ñDz…p(/úJAŸO+t—^†Ç•F,áXN¹ÁÂM~”©pä0i‘/=,¯d«GB æ²ú”ã+üÒ׈1M€_ï(?JÏñHŒ¯,šQ˜«¾c 2 ¹•$g¸RǨ²(m„¯ªÆ¬§e,OúxRcFÄá~ä7žp)Në,˾\5£3f  4è)ªÛ¬ 4¬Ôï%übz8àyÄ͝µ†V>‡àÞC©;hIYç*YìÂÃãÂc#¡Cø`‚@gÂëiòÃä:™)Â*·“C,† ë.»æ [L•Hi—ÏÄ+<ÖmŽ>(ð\C®Êx1I»8ŠÐ…¥`»¨×4áZ(Þý …n¦‡ùσ‰WF§‹.„¯òÑYOY.„€ªüÈËË) €7Á„ðÏïØž¶oßÎk×çQ Ø=ÒZüà`6­®rúÃu@ë‹å†¸PƵ ¢.`Ýç0îÇòÒÌO K‹8íÅ*“>ÅÈ—£Lcð£o•`R–Ä*65Lß‚¦Áµª™Ô„ GFÐ`Þ‘ œà0]ç¾[Ìü‘I'Š`ª¾È‰"–5&€¸2Þ*%’ÅuUu¢D€Ù/+¸4ý’x^T;žðt‡/©Û>”öÑ^X(~ˆgÓµnF@û¨•Ó´AÙå˜BN˜¬mÇš|ÆvVQ41I5½¾nŒx2˜ &]¦—¸âÒxñ/\ŒvVÖj-k,«àÇé2´iq¼HÆÆâǃQ °m¦ õçšâ—oÛiz¼ÃÀ“fKkž˜œ`6]<@âê«55Éy@ÛÎoaÅ ­y>?¿-3µV€/K±Ý=ÞÎ{îÌÙtþìÙÜÅ‹¼˜¶Úÿ8± Ÿ¶Ùº–——°Hºa%ØL€¯õžœöá|P0¼Œ%, ÒöwQÄ ÿÊÒ2Ê…~Xà”6âöS–Eø•›B¯°¯è팂NìGù¥ ]„m1®„ñ#Z¥ED%`}ެÂ)[” ^9v7Ë´œž# {U¤ÊËéUghQåP;Ì* 3¨U^¨`M£º /^Ƭå Üe`žÉ${uõoc}æF|+6C›—UZÉÓ¿¬ÛŒ(ãu_Ì4'à ß& û“޾£Þµ,f(Ì®0Ih„BE4 !˜BÀx&j$Wm²ZÕp#*¸eÀ<ײ¤ÉÈ ŒªÀŒ%!’iæ Ûà¸-`FalWÜ @â‡*)•])Ò®ø*²v^ˆ«ÉQYVሇ”Ø7hôZ„m 7§ù),Öå3³3i?v©?=;—æ|u7¦Ñ2}zçÙtêøñx¿ÿÙÓ§¢}ü iÌîþä—?ìÀï×¥3ä=}fÔë%Ê« mý-LAí×íPY¨8Tᯬ†¯EP6¥×ÈÊ¡§ïøq‰(„º…PRUQ’ŰÒ•¨>ßå?‘¹2I|\U†HÉáÉ01LÍ&¶dàaUq¹’ÈËUÄwÉÍõ9¦ºñúrB¤EVd s`æP…[|¥Þ’W¿•þ%m,Uç±È±hq4Wᯖð)ükQÁ´¬iO‰“JÙ|æöï ã—b-²†© K¾ ×¥-޳²ëpwà1¡ùdAÏôõ}—ØÁ C(ÄΕ™¨¡õœÊAJ_‚(ä¾ÛN?,×ø˜Óã5 „ða)TÊÁ´8 $îË£Uhî)¨ýÜnë·xø­6§bÆwÁîp ÌŸd–ÞŽ§åŽ>3x}ï^êe€ZÁnûÎi&½kü‹ éÄÑc‘§PŸç•àü2n´17ö²Æ_\àu¡*#Mû.oúaÍkòT.-TZJöOE¦>m–†ö!„œx(KKOûTÐ~”x›€ ýˆ€ÒÀ;ÊS^’Us/uP­‚EÐ×-ÀLÛ*_cudëu®‡n ¶Ìø͸_Ê_NŠðXùq\ãe¿áÜ.¾ù—O6ÐycµùÚ9ZèçõaOÝåMšœ7ú¾í¢’c¶Œ†5xCˆò™¼ËöráÅý€8ÂÃçGXaLM\f}¬ùÞäçòøÒj…¬rëË‚<ó›,sjúŽa…ßFde #üyYa P@ ¬«Óƒ˜ca…ÄtÛÖ¡€h¾w)/¼ŠÄG,Ä*/a Ë3ÜY×6»83K?Ý„ŽŸ‚JC6늒òqß p„5¬ûþ–Û®Ý{Òž½{²‰Ï‚¬2{óz§€³^gtÛŸÀ¹DZ8ðiú‡ã«¼Â¬×/aÒƒNÎâÀ‡Q„†M“•rz®¦Ô%Ñépâ.¢â)9–Ae¿+øQ²Âë çPþŽ2UZ„×s£Mc°%§ìŒeåö3VчñŒ+„ ®?Š_þ›K.Ø *lò-YæJC•`5Áw¥Î¢œÍÊ“qé7rŽŽ<,ðS„¼¬P=í ™¦ýþ‘Øs YÎo0ÇPc#ÌÙ˜}`>ûãѤpøÙøg$g?&/„§Ë~¦)ºÄO?ì_ë–é0ìĉvPA°¬¬°Ú›¡‡Â SÈUHúA:üx¯¾UÁ™p•0]·Í ›uy¥Øaò ®‚Ùè f8Nd†Wpl×çî\Ĥo°[ïÅlá]`mï]5÷—÷õÍÏ¥ílèùÕ*VƒG|¶¡ÛY­frwõ³¹ŒB›œµí›õÛ¿Î&}žÝe™Ä_ÉôϾF¾J K€æeéUÜ:ë´lñGiNÝAˆ &`eAÁ:2r¹ŒÃ²\#±*aáõ0éðU… W?)o ߆zÄO^ðÌU`.-³ùø¨õU[FÛåÙpÙÏUXúBœ…:`‘sý‰´°Ð #SñZ|2Øù’BîCå2däzb¾…·ŽVô÷jp!Unƾ¯ÅÐþÍ f=^!Ç(âdè<ûf!iaâÛˆÐÿ0ó0[.ÌT³°]…¾»Ý%¯™Ö£7g_gÊ0™:ŠõA¤_”+iE¢jTF#!^ÙÈ6AF¨½£àÑ¡ÇÖcžfµ³.O9Åã² ­2ÕlaT^âõôÀvb»Ç޾y~øõ„›}…ªn•Æ 0˘ý ˜ôgØ<ÇF ùZþØÊv~ú¸Å‹ é"›ƒž(ØZWç­u³`½~,«¯ðSÒ “é,…¸k|Ó2ŒAÙGÇ·dÔõkÎq„ æ˜ø¥`v9­ «pÒ®Äsî|/[ÚH°j+´‰~ãçÆäÖDÚxc`Þ*ëjðK?9VÊ–&8^…&N:…BQoO…ë “¸2ýƒÇ*Ø¿ â¥4„#øn•±ö'3ù9 &R}6œá§Úïd¿·¹ÑÚfSÚcåR¹è«:CP˜|OTmµQ:› »Ì×fKèè.åï9ÌÜ%Â0½c„ ˆ÷ïé1oÓ;ø‘Îh^w1•-+“6šm܇xP§QîÖk:kÒºŸ`»E¦˜å+†NÇØ"@IDAT‰öSÀRÖeYÍ(DjÛμv¯”PÅ’uI…+ #j¢¨ý⣢°§.T©üÀ¯Ð{ÈËû .è|Ýv(ÚîÚ]§U MܵÎó(‡ ¿»óֱĆŸëù³§OG›Ï±ñ§±„RÈK ÎòYß[VºÙ6»Z,{oúh=/=ŒãË Ñ_ÓlŒÎ<ý*-%¶&0› ãúí—3RD,RBff—³. %–z ü¸/Â1Wb¥%Qaæ¹ho€–2ůʗ²#tæWüiÏàJz;BF@žvÏD04qy8x!¶`L‚dºôųÍú|â8‹ ¬ñ# -.–…E ÿ{=}0È¿Ú wƘÕÀ%þ6бd&"û[Bqi-ªÒ •R¹Eú¶6’øuâ×õ,³¯ê6£ ‚XYd¦õå´;¡­¦G%m\Ý¡ókÅóB0ìùs®gÛþ\x´¯ÅÚ_!Í'„ËÆa,¾mW¯ð›ì£Ùu*t `Þ0¤Ç¡<¤N&!¨P±l%¼Î†â³ì ¦¸{èg©ÐÞ”¤Œ.ö :u‹@ׂh’g[È¢„s½¾xQEæÎ<÷#€÷HOX"ñi¾U¬(K_øa—PkÌΟCœÅ`yëN%£y/ƒù)Bm®ÞpäÉ A/,Ÿð#ZÅí˜p~‡'Í2ÿDqy+ÒU%œƒëñ\^:†¢˶,²“]®`<÷Iá S¤‡«Æ= ¾$l€%V©ãõއK-€©s3µÞÌÐ t&•±“Aó ½’g ­.÷­¼ŽºMÎÁjÞÌÄ‚¬³¿ÕVœý XËû7*7Â×:‹4“Œæ?øÜÑiQyÓ¤U~E^uÂïñ[iÎüu—È<7ãmPùo¤’í“mŒvÚöÈÒ÷Õ×ä®EØIÕáèë‘[0žå©#3 „gÍüÕd 3…³s“öÄÑ£±Öß¶#v½Õdæ9TvʾÄFUðÍhÄo²æåDtÜY姬|°k¡ÕòBËV"òÚ¸ùç0:ˆúŒ]äI³¢<â$„•/œÏ èÂ@‰ƒ«Ô§pªÙó,Ì ¬K7÷LS‘,/æ+»Ò¥ƒ´ˆXæ&¢.WÆWØu^Z8Ÿ/ ¹"aÄÝ€Ô¦=ùöM8ÓcÉg8’rzä[aƾ2œi‘(kUÎ1ЉH/¾«¯* BF~‰FîxdC¡u%Y:ÖgÙ gIº’_plȯ{”‘!K–|2ì/.…U@[—šíq 8“Ë×í6w/&øåÃÚ4{V“Œ+cÎÆ@LR˜ê“,ÓØ³qÖ^Y^ˆ“ß¼¨õc‚°x}sL8•Uá³*u”ˆ-tS¯ã¤H$¬bäÈÝ._Âå¦wƒ÷®Åo~PÖ—¼`ÕÛo_øG½DOUýÌÑâÊÍ*€2f*­äQ0¨ ³®¼†WkõxWÆóˆÌ£1´ÊÅ”%ÌÝãGžà||6íܵ+ñã¸h“Û 3ûþ"j;;ì‰ÂD\¶ÑïÅÕ\ÇÚl.AyÔJ@ó S‹2…Ù%º/ÜîR_Ñ1«¹ÞL@‡£"¢+e †àëgæ æÐéû~}Í8„JÀýÈûæ©ÌÌ·/Ë(;–-XEð5ÿãä ‚÷º®k~ÛÈ?†à B±~ÄSpêå#>Šð!-Èf<r:ùÖŸÇ&‚U á⢠‘âD•IšL8ÃUra‚\ÆúF)Ä•½‚ò{ o©çJ¥Ge e×ýÌí$ä‚!«*­§0»?ÒáÖ^Vèù ZÙG÷¼Q¹¤¢@+ÝÂÀ·UÑ6:¾màc8FÛÓ áì<ß#á€7i<§ŠÑî°*KoüN{Ы”ÏȇG‰£J9Û¹s7àX/7Ú€y_RÅìÇïl›tÚ3Ò.T>‹lÛlx) aªïIÛ¦¶¥Ýs\÷ž˜O³|vsÅz€€mã¶åê¹tn¥Ç[v'Òw\Ÿ^pÇéÔɇáÙ¯¤3§> Ùq®ƒS~Þ7jµü[*ˆ¦åò›¼kDx?*]IÌÑŠ?ø×<ñ2oÀ|žàYá-ß°’-XÑÄ>“ÙÀrñLþx´¨|!¯ê®UDwب:9Å—BYÃ\wvŠK>hSϲÝÅ_‚™ßú¶·§ï|éKÓ?øÁôÄOÄÌfk(;ɯ›ÆqÜ¿íméƒ÷Þë]qM¢…½>«†…!Ü®¹Xd óÛ‚}EW­'ë³¶bÝ̓ÊàJšÄ.«„äéJA3î{ œ½Ð› òìƒbýãÄ 3?`2-ÍNWN$ä f¾+ûàŽ{]€å­Óûø^Ù•1­M|šÿ*BÃÙ:È7úÄጠ@% €Ú„ªÎ`tÂá“ø d_ìŸ åÜœ˜¿Kýãi’QÒ†¾ZÏf\À9:Êl(u£zr¡øŽ®€&C%d9GÄñJŽI Lw6ØÜ;Ö*­s7•Ÿ©N³ì˜ó›JiŽ™ÆNNv&ÒLk6MLÃCüMÔ–ù„Õtzát:uæÑ4Ó\J_ï,±ƒ9ÿžÿøÉtçÒk_öÖôÕ¯ôÓ oþ{éKŸùl\äòR›J@É‹îÚÖ®’Í !À~žrV·Ìz<Ó+Ž™é„ùqYΞB3— Ùz‘çÖ): A±3©ø{-ç*¼%»ø6ç²îš!̼Â\ë!„å9;ÛxMÞGWž³àÉvH-½úÕ¯ŽÙð±ÇK_üâÓQö²™œ•·s~>}þóŸO'¹ »ÿÀ4Ïñ˜Ë7þÄO™-Ûß±1† 9ã{Ö­&öaD·^çåeN(U®,òíÃüÈlµd ¢ÚÎ`£ÐY)H8 Mbp&€xÌ&¶•º;£;3[ÆSØŸ:/<Ðs•o^ÓE"8òÆ%`—v*ƒý³¬ÂîIˆï/w(5|Ûd74Cà|‚4¾Ë¤‘°á……Üà¢ìzJÉ—3Þõ<ë¾’‹òíë:¡ˆdÆ®""Pi"@”StâƒRÓdÆOfÏfmEƒÒWÊ4jyÿGaoµæÒô ³ùÚ—2€›ž™§ÙŒÜ=Óì¥E– ÕãiåVû"euØ“A!7yG„âCm¼}™)—Š'Xs7ùx¥û;¿ó•éøñ‰_Âf9š_,ãx9§0Åàç¶Ûÿò±/9? º1 á s¹xYŽ˜Ÿ…¸Œ„æ9îò(i:>© ÕȤÖ©ï„]¬òäìM¹kRTˆaØcUC¸ÓO“þ> î•?ª^c/à,çÞ÷}å+é£ýhºá†Ò]wÝ•Þò–·Ä½÷ûï¿?}éK_JÜüâÃïž³Ã|Ç‘ÇO?ú˜?vvìÚf¹0ã#°‹‘yì&œ¤H=êwʯqÚàlÜžðŒ• 86"½l ¥ÞªéÁTù:r>ž‘X®GµbL!™ýðà…[SRÅ–ÃaPÞ² ’V…c}^-_êPà—^ ¶¾Îºe •ƒ Ä<-‚ø \n„æ·lþ l£.¾+œ‘P¾„ —à¸iþ(>T…K^ôßúhq¥O@—`òeqÕ¬§r%5í>e~åQ³À«à×YÖIÓÄu“fƒŸOEè'&·qÌî=ûå?Í£ÙZ|íû)+ü`m­ÃéÓ¡<<[?]MF1X|k]¸“™vze®†À9ι­Ò, ³çð>Ÿñ{¿÷…ÿwÞþöXžmgïJ>‹±£#•À]Ö|áÆ?Q/iîué´pccHÖ°cáM”’wèÿô)ŸNE¿l4¤3WSؘÏ/••ŸPC€£0èaâÆQ g1ÍnÇ– ›(U!VÀ~øáôÐC¥Y6ï¼óÎtûí·§{î¹'¬I„Ó½`Ñà"‡Þ‘7®øÞ¿ý½é ÇiQ³Ä1Ý}]à ‘‘\ÿ+„ “÷ TîÞªóÑÙü”$' (—2O‹tBW«€€k_Êy–-Î<°ôÉ…ˆua{ÌE{c­sSÓáÅ%#Ø&iT–…„uöWàÝÿP…B ¼y£™Ÿðh TЦT_ã òøK)Á3—É»ÖxϳM‘§á1(ûX6±¶5¦P¹œÑG!òéAW”6^þò…«üˆé̶Ho°/‚8°¼ÜÎ8’¸cVbY¢:s\·î È´ï>ˆÙœš‚÷jxÁúiÒ¢¢ŠÔSUTCùª½1NÀ0dmðûØöÁC‡Ò—¿üåô‘|$€¯]LR1É` èÛâ w Gæ\—û[Z±øÑÉÅ«ñARPÖÙOèûÊõ©ôRö^ö²—¥}lÞÎf‹;âþð‡Ò©SþÌ„€PšâmŽý<#¿ ÅñÇòÇéž¿„uO×¹ƒN»ü–à5|Ëzlè1K<°T™nq=–¶úŽÝ– Þ>Ô:èsöêÉBà‹nÊ5ükšÂŒ±Üp @Y—`P,¹T!&Ͻ ½ŠíPo+àÅBN¡F!O "‚¡¢Pè¥gYóE*ñD£¢i¿ô+j,ñIqð^Í­ç² ˜° '}tÁœÎœ2yþ ï/Rõجêu0[¡f5ìÕÓó#g}.GÏMò΄L£€'~1®2SS€ 4>YêñçrZ^x‚zŒs¤Lœ¡>éLÝ6E>_Qu´ ªM Ã”w´¸jkNa(m.WúáÛ¡´dÝÇyKô4µn¾ùæôßñ±D¥PàÏxrÿ —ôT_Z0ò‰|*Ï(ܧ¾ñ"ðúZ´Ž©qh}ÊSîeíàäa'…Öì'NõYÖ(¾!ü Düštá·÷HtB°‡¸±a7&Ï#wyÖ¬@ÛŒûFÖïpb‚ȇÌËñ½?N2¬Å«´‚üµGZáÐÁƒ¡$²..ºà{=öÐÁCa¨m?þñ§“'OÆ^ áU¯zUzôÑGƒè_ûÚ×Ò2›9{öìåNý™x3N C‰<|ÿýAl7kfØõõy|ç+/ÉÊjRÏܧX;Æ 8hŸ&iÌÖ•Ð)ô2Ÿöú²Ž¬µ}Ø Å‚%¡`ƒ&ÏâVì.<^\Aw5B¦!Ð. t2V)ã–WÀ³àg“ß} qĘCk£t›–c|Ð(&¢ W}oŒ‘XåGºá¨¼‘Í Ý1ãOUQ!Ñv|ÃÎäuflMçÝ3sãncÿg[šžšO{øƒ‡oO³<Ý81¹‹YóúôÿøAÎÎ?ÍÛ›OƯuy”¹¶È¬‡ùný*V„Z¦÷}5µíl 6QG“*_áŽüvlCÙ“nZäâ> ¸œˆ„‡ËxÄ‘?NH.[µD_ûÚצ÷¼ç=±ôT°~åW~%ýÜÏý\L\Z³ÅœòµÇLü¨qùzèºCé¶[oÅRÍ àðá8^<“îýèGÒÇ¡Qì11‘¨8ÂÁwÒ(îǘæØk™ÒÖOX¤ãtÍ·­çš@Uq¼SU“ã†m ©íf†/¨p=/›j¢M"p2÷võÇ]é€Duf‘àÊMÂûî»/Ö]ûØÇbãÐý‚øˆ¼Ã‡‡ð[N¼:w÷­Ó«¶G9ePÔ¶mÛžæ¶q¾»w¯Ç{çx[Ï€Ë6ÕÂl\2¨¼]È{×Á—™òL$÷ XŸ‘Æ@t»+±FsÐjGŒ1ÆEöb¶©87ž/@xì«Ö…ù‹20ä¨s\vfU@µ‚Vøaúƒƒ¿Ã¯B£´ŒóI ¢˜ä2´"ÂUû«yÕÞcÈO>„̾ŒK 1ÓµÚ\èjíI»xbq~×4—¸X·¯ð6eMlÉÅ?/ÞO³ûçÒ¾çžtÃ}éñ§ÒÎÙs鉇> C Ć'øÛÖoß ³Gëlc†‘.j©,‘Š€æhŽôñx+~)_Õr%\ÆÃ±UPöúOÿ)ÍžtâĉسV‹ôMozSzç;ß™~ý×ßÿi½ºô„'ÈW€÷ðx÷õ7\ŸÜ(TøÅáòá3ŸùL<¶²Òe_á~–'Óù g±FS:Æìïƒ_Ó,9ïBÖT¾N,É ãc(¥vÇSÝ äM@»ìÇ~,,Ï_þå_Ø8zfÜzøáô‡øÇkßÞùÎÿ%žëðê°ƒ¶Ý'9W±lÜ3X\„Ï/¥ÿGÂäBx›gvîœçì¶ôòW¼ ±–ì¿#½÷ýÿ_úê}¢ çâe5’Àa•JnŒª4Ã:sö7-hãÄâ«á)²{ô-3^ôê©¿®El\:ð ë⩽õýÑÌ vâ—|’Ïë”>æÈ?Íèuûé OÀ)Üޑ­ë~ÁEnÀÝzË-éñ'žÈ&kü“ÑAóã`YNËàý‘èáÇüǯ›ŠÞ-P¡è¢(ËùŽÂ—|Ç‹ÒïýÛßM7ÞxSšäBm«ÚPߺ#SÄLF…Äe–â™ïÜ»›¬69Å qž¯(%ÞìU`›{âÜ pÆö"ˆ‚L”’.y.wôbÉ—öSºšfØOVãÂ/a£T†¶Ä ιåiº{ ÎY¤ÍSjíÖ¶ÔæÝ@™ä&\§?ç:.µ§çÒdíTZ¹÷/ò ççy;&ÞZŒ*ú(j%Äþ›W*Ö…ókiÿþ}aõùS¯ÇQ³¤†ù˜òÍü†OÀn\0 K']ñK~‰_)onvÞ°/lzù2È¢Þ½¼±é§ú§Ó;Þñ#éw~çÿH.9wòÆ&ÕäÕm Fúàÿ"xì£ý¤†ž¤-qØ1öMOî÷¬q”¹k7Ë¢éfºã·¥ƒo†nÝ´cÛÍéáÒÑGVÒþl˜þÓ½_IgO>š&÷2mßËÅ1­MYÚ69ÝèÅ©Iæ©â­Vybda ŠcŸ Î&2«]î'g÷ *€Šùì0O¿°ah›Ï;׃:kkÝXóÝzSS. òzz\Ø @aÞÏ)€ŒáµØ—ßóÂÔ[>Þÿ±/‘µuw©`ÇŽ‹[…Ÿþô§C[ÿ÷zá _Vw |êÎÁÕ©TBîMüäñéŸÿ¯¿Æ ®¦}Æ^/ÙlqÌÈØñOÇ™Í@Ñ3ç!÷û=5hqÖìòÆeÖ‚æ±;Å • ×îÅ«âëcˈ¡ThCQxÑk€íϸ šï ”ÓY„C‹Ú‰¨ Ê™rEõÀôê¶íw±vÇ£q±¡Æo$ 9R[ò•‹Tã±(K±3œnpC®sÇ;;оÍÙåLî”NÂñí‹SÆYE¥œ?w³ùdúÿâ7Ó-(õ7¾ñùf$yöÏñ,3½8а.}1ýÒ¼SòÆ}Ëíè³pÅ,w"0®/¼Œ›¶ŽYôÓFáŠGd,^Ò‹_`/çÛßhÂltÓ´­«X„*táøY6‰½âróÖ[oWޤ#GǺ|/K›³)²oÅ¥£ ²ÆYüòŠ{>Í´w×<—ÕP;fÒM7¾‰7/¹­z/zÝ“¾þ¥ÓÇ>˜>öÑÓéè£Ó©ó÷¥…µ3Pl´äuñi? ¨³i÷„we–Sï…ø5§m*(éÉÌϲÂxYB1éäæ ”}ÌCâ狪þ!L<ð ZÖ Ÿâ êÑŠ!Zkk.ÃPî$köÍÏLbÎ3ûñó%B'iøùÎöÝ€ùûwÞÅgβ«ÿÙôæï{uz÷ýEì+Ä CʸÞ÷ ý?Åzí¯xE(Ó½´áìÖÀÞð†°ÌÛ†òÁ¤O|âik¶c(÷ zXöá(Ê@KdÇÎ]lnKÛ¹ª‰«sC¦Ã:W©ðÂÑ&ÅY=’ÙgSÑŸlOan{—Ç&d4MÈB¥_¥…²ÈDš#B/ ‘ò¼‰B­;3qñ]…£NÒŠå ˜Un¿y–>À”oÃ\qÝ47 (˜Ol1.μ*:ÿHb<©ÈÊJB°2;뇙Q“yÉËðžÝ»ã„ÇÍR-¿²OcÙ¢8l^‰Ö™Wœ44¿ÌÞæ•²%ÝY\8gvŸ Tà5ï­ßq9Èé“/d½îðõéàýðomF <ôÐ#X‹÷³Ëÿ§éȧàÁ>ÖàGÌ·kéúy,–ÞþtðæÛÒM7ò@w&oxuúì_nKǪ¥O~¨™>~/÷n—VÓ±³<ÿ‘®£ÙŸƒRXÞt¨ïNû¦öÐ~ö—˜Pú}–[¼Ez¹ÃéTÇÖâ}‘‰òN䦚£¯ùD)&–3é¡B–àÁuÇDÚ“;c‚: ¸HÚJ>…ÙŒ»6 cŒ‹ÁqΪµÛò¡ïÈ›]ò¬³¿ïÕw`Ž2#ÏÀ:ÍH…ÞUh\Gj<üð#±ÞÞµsúÍù/ÓK_þŠPâ.NEã+Ð ü+_ùÊäá3ºLaþ#lüùQà5CÕöwÜqG:Ë2Àʵž³“Œª³ý¨¼ltîì™t”ýˆ¾Þ†Ùc{KKÜ’Ü[™>ÌÞXW3+R·Í ¡&l[tNH³Q:²A1D~-rà£gŒ0 &ü(›Ó#ß qÂTá CË>åÛõnZb?Æ«¦žº¸,T‚Y‰L5ÖvæonîS1á| ¾¯µå&l1ÁU Ž—´ÖbS@Ë,]h”1åq> µ0ú¶¯,åäÇÀ4|妛uyžîX«ø÷—¾ô%,EŽñ‰XGžxâ(ÀgãzúéÓǃ7Ø^ýš[yv ‘®?øålšoͧ³7¼*ÝtˇÒË&ßÏ8)­Õ/¦×<úÆôéw³,¼—QA°Y¦3³·yÀh2íh1ù2s÷ûù°W4h¥‹+EO!‘E«Ìαlx zre¿¿Y™Â²ð/ñåEl=Ç=éí±tA6ø-^p›ïgålÿc̘o qq,®æ|F@á–„­­¬œõ $ÚÔf£ ÞweÀàÑ×Û.xðbÓïÔ‰Ó1ÃGëªûÌ•ŒÄà8ûk&jjÝvÓtúó~$½íM¯@“oK§Nó²MÌËØg€ Ü4”ôwñ^÷ܽ-gú¶Mær¿àsŸû\ì ø‚&¨¿ªóƒ?øƒ1+y¡Ã‹FÂÉl:™IfõÚé©“ÇùðJo—Ï)ØÆyfMrëhÔp6ÆøyBøƒ9+a¤1F!Ð!Ìy­GB*T¡Þ%©¥¬@Ä𨬀2QV\IË±ßæIóĺub 󒇨âe¨u®€o½Œl†Z ÷Q<¿Äõí«´’®ŽkhOjÜs‘Þ*u-‚ŸýÙŸM¿ök¿4—4.3·‚^ð;¦Æx'ñ˜æ[—{ ò{0ZgÖuÏ=wGùÃÌð¶áìÙsŒé×ÒŸþéûXæ-b…œÅÄf-%³ßn¬Ãùtç nO7ÞðÃÜ)YdLïL|mozäëtßçfÓ‡ÞϞѮ•tüOé_OþÇô²Á¯¦•.B?ù¢´xº‘v`ö·&™hàsåÒ!ëòÌËê›Êi7ŽKy¬ˆp¬^÷ Ü”þÄúZ{Ø¿‰_ºÚG›¸s©ÞöÖþ¼o@‹ƒÎ#üR:ÓC"¶CW9Ø% ØŒå8Én#:S³H}yï›±.ò@Î6>v`ºú+ßì‘¡¸™¨6sÐ'¨.pŸ_sÛ_Ãùûï?‡—»éßüÛÿ'f]M{…@&Ò)Ô¨Ö#MŸ¿ÿTºñ¦QŸ‡a´ì$EóË42ŠO dçy×ÌÝéßaÖ÷fñÉ8!ŒÀËl2‡LåžA\B¡±o}ë[ãœWx¯%»¿ ŒN&5ÝÇuø±N_fâ¬653KŸ*6¥tÔW ‘åŠ3<6^Öü Æü/þXŒåž*^ÂëØK-øÖ=7–erÍ£J.𠘙¸´ðÖ§+t*ñKýKaÌ/Â/Z…ÿ—~é—Ò½<Ò]ò~z¿ûÝïNð~ÿ÷?”°–B±Ä/y@ a7æûuÜ›bóíµ¯}Mº»ñ&(ê}ø‘‡Òî]»c_I~jc™}á‹_æ¸íOY¿³?„ù½“ñ9|Ãþtó-»¹xóƒ±`û’“Š[ÒÇî=—»o"½ïß7Óç?å$q »À²E5ʬîžÇô+ûi/º+í%mž‡†xGáæþVêÞÄ>Êg§jsÔ ØÝùf:B/¾–ŒƒGÞË ‡óÜB÷_ÄØó»5–ê¯f žEpãáöiWØ’1b ~F[;€4þâ¡6xÚq«¬ÕxÇ~WóOïDefnÞ}# @ìN,f-(ôÖnÖB1{L4é1 „‰k›Ðu’Ýô 4~ï¶ÙôC̾wß±7€ëi&c¹'°€VFB¹ †•3éÕ/:œ>ùI@¬E=5àÎæº³‚ù¸úΗ¾ ýȽ3=zäÍé/þê¯ÃúˆÇnûú—kÑ#GŽÄÌô| ön¼ñÆxTùïxG3zôã§lÉÌ*rWß—vø1mŠ›p{8ëžãLQ a^¯.ÒŒ†€;pJ"Ÿ"ð_ m„¡ñŽÑ^ì*Ýö”ò$Á{²sOÁ“ ßÙÐbwyÀÌïumÐðÉ  ø–.Ê`îBÊ׿>ÝÒ ^ÍЄ|÷Ûóão£s€v„§  ƒj?¼õ+Pä~(rJmoˆèÿü¥ç8 ¸y0 iì&°‡œÁø2:„özè$~–ؾN@#SgÿN+0Ô¤Q5Ì+9ÒÓ¡Æ „ *Tò<,º~:Ý›]¼-à@ˆäÝÕ¸c'f@1ø©ªúß©çuЈ¤@ÍíØ–*e=ÐWM{^Ù™>¸åú@ææAà4hLà l¶í9š~äCïx¥´çRœüJÌÉǨœLõgŸOïݲªƒ§w €ÐÁ—ßÉúõð#Ròì .[êæ|UÁ Sç¯ “>þñÇ·T'*pô»EÐ 0ïð ¥¼"¼Ô;€[€©NíŸÅsû<âyB;M?#ÞÔÑ“üÀäxéù­½ãÁþˆôùö‚_óç±²…vÔš@s­ºj[ÆÉK^à“)¿å,ÊPô³g©´mTó|§_ V?ñ‰O7öoÿí¿ Õ«ÜßsŽn±Õeû¾Z“§žú>”鼜5yú©g>í(²vřǚ‚eËæ¥ ›ç§ WÝÇ‹-×Ò°.m}fGÚ¹µ’žá`Ú±mˆ¾ïI'‡²’~{t¸’„±WãòÔ>>%ØûIöY`·© 6ckhÊ[ùŠ#µ“ƒ²ÚHw©wí¿›¿ç÷0 qÃâƒ:ØÃ4k•®V•où…äRzŠó‹"½_«ºf @iª¥%`+¹ç19ë XC]úxŽ  ¥‡x¾Ë³X\mŸäaöye©°d|Œ‹üqî¦Í€[~Bô^½Åð¶@‘'#¾vîìÀi··Š’Åá²\}Þ b!1ç<84B‡ §o?þRúéŸýI²ú Êf–Î= ýýiŒ»#SÇ¥M‹X5Õ9#õ Ä¢ÔWªã€R^Ð —ƒziÇÞ´rÍÊ´nÍêôtïKH[™¯1H Îã(9ˆ}Gÿƒhz1 œ‚„ïBî|®àPê¯-÷† Â~àÖ[o n@oF{Xx¤‚ÿª>C€F¤“Sä#]­ãrìô½€_KXŸž´ø²7Í«HRœM׿\ÄÇSZÎÁšs42I³^(³ºûÈ  7O©¶‡“‡Áé]ŽIä©VÅ…2Ú^<Ê2ï?øƒÿ3€\@·8ž«Ý‰Uâ \`þÀ sW]Æ55ãáuêªUë1Rj¢­s¬ˆÍQæÎY›v·§í/ŸI‡v-Lßûö¾-QF?xh;ÉzÒäþ…%5p§Õµ³*;Œ«ˆ餴Ÿö8F@@Jý|¼ñ™&qUÖÐEuˆÉFŸÕ où×®d$ÃH //”›>Äk=ËÎ&&NÉq.JtÁ—¼¹,@NÑl²‘+“ñ¼ ЭªU‹i0¢z É-|ýÌš:2çiìLÛwH›–)(¡´ Ž®.-§r/œ;ÇêÁÉŽ´båÒtèÈáôê±á4yvßÙ1°TÁ °R×q’’ÜÖ™Ÿ8…¿ýÁéƒï¹>=ýÜKP9ÕGÑ@ä¹fM8è7u0r ½éÀžÝ»@B4t[ð¹€á¡1‘Ç“O>™V®\™6mÚ”´8¼÷Þ{c*¡û²Óp±9oþLSxòðÚÁÜçâÙEèÀÏÆ³â97¹ãIí‡üD ÓiêŸ]|]7¨ELÔFšñ¼é dµ½­cÒ ôÞˉ8fy-2PmªDÙM›6#{O;vnOŸÿüç`ãÏ¥ ±…ç/«‡”¬’IK5é…-YÖ XM«W]‹ÚhéJLý®N/<=‘vëKu7aH3+ õ#˜ë‡‰–ã:LÎx^†i¥m൷\†šOž‚›À®Ö~dl)\úu´³@ž-E¥ÚìÙÄûÇiG€´zœgG8+î[FüB§Y5ÍE„Ñå•4õ.U:ÈÿßOüb=‘ŸI–®Lg9 ùøî ÎWq>Eþp"LRzk‘CãW≩F¬:5Ÿ à@DN åô)—a$§ùõÞ`ßÜ“ÈduÝêóèDÀ œJ¯ýW—¤>©ño.¦u@2ÀËc F”OÉæAqàô¶ÓÎ`&· ö¾å–õϰ{#cÉâé(˜]‘TÕ ”>dèr Ãý麫–¨ ò‚ëízRu½/[±Üï¦_>˜Öm¾:Þ+Ô'Þ˜ÎõEJ¦Ý¹g#éþÙ‡îKãC½é?üþŸ2¨r3¨ÖGŠ—„ ?ø¨‚AU†jd}å ®¾ú꘳vaxð/Ø)ñ}3• Óxcšé§\‰ˆ/žÈ"bòM한¢H—#_ÿÈ!„ó@ÊÜ1oFš5QN'±NkG;S†J;~Ô¬h-ç#ÅŸÅ>….ãU€ç¼Þv¶®æ{ôè!ØøÇÒVÖ]ŒcU(ânnÂȧ_7ÛXŒ ýNœÀ´´ÌåèH·Ü°" Aƒç_A_nL/ÁùoëiNÏ<–ÒîÕ4À"¡“CG Á‚9ììöÝ©£ªw&< ¥œšbšI§ŽŠœ+C9ÓbŽå¤€À|YA[µ ¥U Ÿ^M¨Þ|¥ŒÜ¯âà;åD«ÛÈ 8¢9Rõâ׉»äw{?ˆFG¥Ô:)}îÍÁð‘œÚø”öŸßò,˜CÎ7s\É5ˆ$|u¾ SÆ SÝÉpýÈ&ª,þi:žÇŽc “†MÊg”þ3V~ø—ã% ùSÎøK&tá PlV€œÿÉ8€øÃ÷ši”¼©0¨ä¬ÐàzbôèèßË`ÀµøZŸÑ1/í>t"mD(¸rÍêPÇ‘GêîÞ »·¶¶·i6ýŽ[7¦¿ýÚc ”ó¸jj8÷Ùƒï`Q Æ30 >z"}äGn†ÑÃH*e€àì¼U °•9`¶¶¼çiáÜ®ôÝï?Ãâ°ð ûW ¾kzƒÓßטHÃ#eZ™)+pº!Å€b*`ßD‡Úcbt‚÷ž  ƒø{Z.Îönþ4ÑÕÆ™Ò³…«Ý×\pŸSÖžÔ¥óK2W¥ÛÏ™H§z‡ëñQ ¬ºÚ—U«VCÙ7Ö€}#sú9XT2`ùæÎ»ÒÓÏ< ×t.;z’:¶Á °ß6££øfXÐ ·Öœ/X–,Û)Ú×°ÌüC»m©¯ycúÄ#w§Ã1‘Žÿ-Þ €wÖ–I‚¹VÌcûä¨Å*=;¶òS‹ÓØä²4  LLÒh±ýœwÀ9tÿø àÛEfÛ8˜Ê9ƒ¥—pøžó¹Ìå¤õZ}¼Àª°Î±uGGfÕCÏÀiœrùírZ{wjÄÃÖ8ã8pM6ñ=.§E†~/‚,:,òiã[·rlæÞohÔM}Ÿç½”Aà$ˆ êž0‰i‘)ÀÇûz¦ÿp‘Ž9ˆ ª@§PYhœ½lÖN úwš˜Qúˌ޲;p³1œ‡ª|ÿf¿1Öøv4Ôñ4sl¶Æ+ÍG_.à«m6óº¼~¹‚Z†ÕgPêõ¬õ¯V›ÃÛïYVîÝ~íšPÛY±Öî¬Þ(©G†ï<µ'½ÿƒïkÄ \}ç\ÔàXî?ƒëç6œ†L˜iÃ,F¥t,2p¶.ˆ ÎýõS8ŠËñÿûþ0ݽåîôôÖ? -€2~]¤Î?ÅÁgæ«£UZ7`ެ}€ªBŸEð\ˆˆ­i4ßùœM>5}‘…ðo:I]~ç3ÈWµ¯žÏª–ÀïÉž[®³¥õk‡± œDõy'¬þhZµz5@7²”îîC°õŸÇxæ,š‘3Ôd:à :gw¦wÝy5€?•–áôcb”M]Zf±cS®§[Òì9¾·±-ýÒü?N *ŸLc ظ£™Â4'íœ{wêÛ†ïÆá¦4¿AŠžšR[ÄhEè5Œ¿ê˜cx1Ïnçœsð÷ÑŒ»(ÿ¢{€)%{.Jå±¢KX8¦©y âÝEŽ%¯Oqì&-óí8$ È«âp ‘OôÙ å™ ip7è¡o!0Œ,¡J¹˜®Lô½7M½HÒ6$hKÑ+‚ƒN!_Zñ–Ã~Ô¨‡óIÊœ2­ÕÎ 5Ô?iìà74ÊÑ8¦HÃø ·tÄÄŒ˜÷²nŸq[+#I#\øRŸÐw 6Xiá²(œqoÞ.ˆ ©TŒVö H{qòFµPØWäzŽ ó`’vÎÂ^¾qvzö¥}i sÆÇ_Ü—>öcÎ2'×`(uõ ”XaÕÞ}Ýiýâ ìÎI ÎQ Ã!ãÜPÉòÎÏìÄæûB`I•`ûž AIJ:]áÝÄUm@¯ ñR(hÐõ§k¯¾*Ýs÷ºôè“;Ò²¥KÒßßžV-’MBsÊÔ¶ß 9¯ì´G'\ÁaØú6Ü=/[Ї™#'™¯Ã£ð†p/fú¡áÁtâô™à0Æ0òhÆ-Ú]7]™ž|úù0 dM:Ù]ÕxR;…X]8exáµ#Pþ×Òî§X¼œ=HG­Á[ 6€œ€G›¯{ÏDEÀ=Q¬ € à63ÒFJß©ÝG6E|‘ç%ÎÅò0”šºG#k&FÒ—_z0}•Ù$òšÎÎYiùò¥éÆ›6¦+×­C60•[?«÷®A׃º¶;=õuë ÙÀ¥1ìsû+º+ëèÓ·-hq4ÈgÙ×jwôpAgÂêW¾•Ê×ÿzjý }&ÿ—”j„ºÉ­êg¡Ê\M<×Ïðf7‡y,"NyŽ”ZŠ.ë.°aª0Oœ º"Ó{ØÛ–Ïx×pH¡äpyÞßËù5û×aï;ëj× í™h>Èþ Í¿A6<®"?qêPE‡”TUÛnKE|^‚•¯ÞB„ÜÆ£”m&ç¹­#Üws¦ŒÎýñ¡´¿„¿¿ßÀ´¬Ü  Dý ؇€ö ìï¼~ ÙGX’[mTð~„èj›òΎœú ~/˜FØVŽQ´«ÁF•1½ò2(ÜßoŠõӮ!ÜÌ´o÷Ø}~œ>›¢Þka޳‡¡[î¹3*iZ©¾ßµÖî+ðb÷*¢t ¶yó¦ôx 2œ>u$€Ûï7ˆhJ¯í:˜n¸íÆx–A0È¦Æ õ›…0þfÕŒŽÛ—Þ}Û•é‹_¤[À¶oþéŽ<‚Mc@Ûyñç¹ #­ËAœ™K‰®MŒ'5 ¸¯þéòE‘Ëùüj ¦ó7?Åhhf&Ä3ØèâŽætÇÍW!@àq2Õ™d'«Û:ÒÖçûÓŽWâàbo:1ˆuóóTí*Þ”n@·>/Í‚ÀI­m#Tú°ÈÐ^®Û~a×½€Ê¬:Â_' Ÿ§6¤—Û_Æ€ë)zð¶T: ïœyC­Ðhx®åªs|çÐì‡]@ l "Žb¾/СÁ HyDU eŠ·Œ+s˜ÃwE.”Õç0Õ^ôüA5Îpòo©8ÓÖý ë^r<]ëIÅo|ó4ßÙýù­àÞò‹Ü–r} ˜ T_ƒ’¿ÕhHÀJC—ekWeºd³]´Ô$gÏô½B@JD^µq#âä¾ÖçaüÑ Æ^^¸`íý^ (à1ja§Tt>¡¹®-Z%èb­W¶íLý§Ï¥ÛÞuB³†ôþ_Ósú,Z‚tûÕ…¥éMˆtê¹}ýáCGP¹Œ¦Ík—`üñJ`ÂiÀ!ý©Ó'cÍy‰Unó êOlÝ›î¹ï½‘×8K-µGp/ Zoá¢%i~þiñ_`8yäpZ³…Ñk°µSEÜýÈD¨®-#2¶âÌ…msΟÈyǃx5žºÏÄæ4ñøüùú îaþ†_»D Z³ò±¾qÌcÑ¿7M¤üûîô­o¾–z޳iFïÌtvRî { ((ÖÐ;Ü\PËhe6jN!Ù.Â9)bš<•W®÷@97#gá; Ìý ©š,oec„°7ö)øc"BÅÎÝ2ËÒËöË:C%áø¥¦K±}6Â!e—€*JÑÃÞÞþ“+8ÁYB P!Û"Îërú@u›od*-w`°´+E©¼ m©.å[í8?uzG¡@’ðG”²ºS5a?#›ãœ—Ãjr8Ƶeµkˆ3 áQâ™ÜêÀÂø'Œ€~ý(×°u6ãç‚ xõêxF. òбYôsŒ˜Â` yû­‡ËAÅ· ž;wJž¹pµX­%UV#`ÃVÀ þù·¾™>ô¦_ú¥_L?û¯~!m\{UZ±`nêE¯¿`>+©\F,°ÂE¨[×±¢á;]½iu\k3fôÀüq ©¶†*¬Cuµ{ç+éš5³Ð"ÌÄv€]„È"U]zk1œEVpåÚ¥ÇdðÈ“;Óû?üt€ò… Ìo§)C@è Ñ'‚º!ÿÚ¡øP̹¬]ÔÎEtíiœŠ¸ ΤÏ9Õ§¬];@Š|§ó6¨ïœˆ°”¾ò__MÇNV¡?šnýŠ´UŸ²œÊ n:Aû²I)ˆ»2æ@^Ä0´X†Ki»u¬¹À¨r0ø <ßt­H©åjÉ—²Û{•o§®Ï|&UYH3ö›Dí³ôEðZj¼”C.v9 D~%_{ÜeõšÀÞÇ7PuÌkŠí\^N@6 G?‡HÁ`èçÍÌbIæ@IDATÀ¸›3«íâ¹2!ÙÃëU9NÎK›È…”1ÚÁÓA:|š= 5„T²3u–þ_ÇYµâ·8/ãSí,¯KUŒ‘´ ¿*¾ªÔ­Ü(bÈí„‹žcUH“ØÃù̵í â¨ÉÜÉ+àÀ¢xpTA€s¼èãm‡ËBµ¯ØƒÌ©ÇNi¾X™¨–4ÔimU ’7ædašX‘âî>»™¼„ù™gŸI¾÷ºô[ÿó¯¦/þýS NüÊ!Iw¬¼>úà3¼°ëhš¿jM\ŸÀÝ”–ƒZòZtÁÄþøWGßÓw.í<4„ñÐÂ@®M°¿uJYhFG±aŸè@ÈågéÆÁ}Ø™·‡5Ú±ãõÚƒxüÆ?5€ÓQhAé‹Ä¡= µSÜ–ÏÑù·hó0ÌC¨Hâ»÷ì%©~]>¼K¦M|´<Õ’fv.I‹¡õ“î”ÀÃ=`ˆûÚ6QÎ;yµöS`˜€ð`ƒâÂWcP#¬¸uD)ri~`*µÿ8ªÕ°ë•§€Ì¬QðÓ Á±ñLvöcX4q0aƒJf. írâäq!t’kËâ4:žmæ<Ÿr/âìA–^cËx為 ü" uô”/HiT‘ŽÓëS sPO®BøVz‰ºáô´ÒŒÏ˜ßO¬C´µG¹}š³å1Þ¢ÌækoZ{åpøHø^”PYn„[@ÆPe%õSªxdVàM„/ѵä¡/À)<ÕMØ@†‹‡IŽ}“ß¾I²K>ŽBY‹L™ú[ íþ­Ç–z0L@Ueµ*Ór¬þÞ=»Óüƒ?J=ƒPë®pìá"­ Kµ!Œ|ÔQ¯[wUÚzjÙ™p©‚< U N9´jëÅ%¸úiáã=iÍ ¤­„ì¾›.p*R׊}˜µºÈ£k6+ÇzÓ#nÇ¢-#„Â62ø?µ.£ƒdoíìÚ¸¨½“c"Ö'k²)Ô’½.”çßÍ×~«øžïÕ?÷þ‚ÔDïLm3¡1LÉF†úÒÀ0ÎS1ËœBÐV½‰!û~ó] zWts|ƒü5ÇWx~œó²™CÖÙ͉JP"º¥r!ã稓 é˜0‰ÆXŸj\—F€¥ñoÁºK슒Geዉ½–c¥…-fý¼@UJ[‰°œV8¿–*·p°c–ß–Òõ´Jÿt·¥oª=¹ìãžùw¤_Á½n÷{9¾Ãu7‡ÈÊJ(:ô Ô"X蓾¥’¥–Rjû8<âmü`¦{ˆXÏáwgsÜP;‹”äRDdZÁ(4:Ò]ŽœLs4>®÷ `÷xvmXž: f~ô|àNÖ@øDÖçÆ`‘¨8Ÿá\½ 8æ·µ…òãg¿äZ¹­¨\â(*sž¯@r/ZÄšÿi´÷®–?Ï ¼ÛÒ{î¿/ÝzËõ©µcVcƒØìÏéä{ÒŸþé'¼M§týæïÂ!õøe‡íEÀ8Œ*oiÄÍbÉ姇¾ îG×­{/‘‹›Ž¡.âLÏGf£Fì:•¶Ü¶–uÛBu‰ÆöͬnñÎ~Ô§µ-¼·Ïì‰Úåôë—êè»ZÿEz9 ã}ÎÑÐÓý;U\œï÷ ã½3©É)VÇ•AxÈ A°³\oç«ÏÓOÿÀ‡ †±}!ß\α‘gœÀ)…zEµá• }zSÖÍÊéW‘× ¹Ò| Ä8OmÿºœFÿŠGOÖÏÖÞÏózÕ€j¤è–Ajj"YuÌeÓ«\‹ ÖàB|&`AlJPÖ´‹ä> -§2‘…„@acg ‘iêë8¯æ0 ¯Ò¶š„W×¢à±ÕÁ)¸[ÔLÐ Þ¦ÚFRu|NDòZ‡x-Àç°}”£ìç,rò{"ÀR±Z•õ×°IxÕ;ëœß ð3BÓePO¹Õ¶âFhðƒ‚ r–g6€!†I¾|ë¿—…X„<.Y@!&hCQ`èðæá!0ÖaÚhÀ3Šo}ÇñܹØ<»?ÀŒ°¥›yþ£|—LÃéç>ý‘ôž÷¼‡z»rÍ'Ò‘sxtYtvøîcz y¤AŠ1<ĶRŒr- /¾´u!½HГëŠU ÒwÞ_ç‚ qº16:ë;Š‘Ô…¡u¦C¥M\}9?"ßâ«í• •ZW ÂŵûHfú"qíåé{1ƒ¡Ÿoø-0ÆtÄù ß•–…‹ó‰Ñ`++¨¡Je¨9úiÐÈ`ן$Û«Hé uÞªÚÍzœò¬ä,@:T2åUs,ö"­Þ1‘Æž¡Oî%¯¿ UêOÍtEå›È ^„¦j3pAÓšGº‘³ùûÜR;´ìc±…gÚɤÓ)½RvÊÙ€, æ='UfVÛ¹Ò.Ú  Y\…@iÝägsϼ<âÈ5©MÊ:¥T]é:ŸÄF•xTu*Uáj¦N@|þÑf_¦ÖQÄ(£eø­‡Ègy‹tW‡8ž'ŽvÖJ‘%Á¥°s°,KA.º±cúˆ=€ˆMK#r°‰ÚѦ _ó‚ ’ÔŠ+xk§€=3´ÁÿÉ@°|ОÒ&ßB™Y:dje£!dIRrœ$± d —TÚðëØðÒá6Jè R-¡¾ÿè#éàkO¦]»w§á±gͪ¬ÿ^™ÎÀ9¬[¹'0LѽXÜ‚ðK;î½ /î8’Ö_{S\‹Iõ棌@'"…Ž¿ŠqÌoÍ-R¹”Ûz0½÷þµq=¡Ý·B `Eë½cT1œÏ_瀯KKh3çVää6ÏÅßñ›—Š3>¾ˆCQ”«ŒŒ± Ÿá<¶²š³,éJê —V 6_àèp¶‚5)²e=À!Kë`ØÖL/æÂåmx>Æz‰jäÁª@Öéò}±ˆ¦ë9 »6Pûx‚Ò8çi¾’T9  /òÐ˪ðvsÞÃÁ·K Ôq¶\F8†•5¯Ì„zJw$‚"4Z¶BÙH›¿aÿ:V4ÚéáŒ5a wqÝÉÌ@aÁ±Þ¿|% Šïª¦±U#ï!,5\•¦òüÜGùÊRE9¬@Lù«!ȜϽù» ɲSfHLI0&üžjB•[ÑZÈgßa-œ€¹Z,_wf·óßóa¤0³F@pß\‹%E>ô…·Da—Â±ƒPÁ>(ü챉‰Jks âLÇ/þÊqœÖ‚^XÀ“j[çãZ›Î!©w¾~ìø ©›l µ§jO=ùDØ|ê§g³Ðûï¾…¥ŸÕô÷ß~:¬Öt#nЖZk*Wøé)È jð¶ ˜©:è¡Îˆ–€µ7×jjk¨Ùžé &ç9¡;"‚u`:è–ü"Ý@ø~" âžñy‘>_Õ½G„#|mþoÅÇ;´ùÚ“¶vܪ±0Añ«.{ j%-=s p¤`¤ƒB%¹¨šÌ+è€ôzw,'£®ËÑc R ÓZl·FE"í\ìlÞž)AÑåDÕŠÈlM‘ÍùfžÀÝ×ø>¦ k&‚4ÐÓïbB0Hs4]€î·DvŒïY^€œ¹’\ N>³}sY±•ä¹uðMÚ¨Š*z7”wûÎóáPhã üT×é#Ì@|Eø)† F^D[Z·žŒ»¢"µ›·sz'Àï¸#è(s‘“Í ¥Õ¨Ü*­³Ëã@±B\ä¼ÝPF µöcéç~ŽAç‹xì¤Æ†–ðꣅŸ²“رÛ<óæÏ ã!7ö<ÇêCåEèÂèìX¶Y0î̉céª53q978©sÒzßâ­KŸ§Õ€>®õV=B«.J,2°ï1Ôõj®I‘·wqâÇÞV TKïIßôÌ÷@š7Á·¿k4Z@˜ç˜3— jéEž€ÄèFMåÜ?Ï]œ)©>̽ÓÙs)¦À%áq€SFNCÓ´ë¹|’sñ†66W ‚ªÉÇÅé{ôÕ¨³ã8 )´lú)Î'8œ0ÐYÌY s„_$€`,Ë8—ÉÃoÑ×I£„Šóî¹KÈ $§à-€^5¡«E4~ÃtÔÙzÔ´T¨ÿ¨ Éì)%ÔŠY=›…RŒ¦vu <çìïÏrjçbzËå÷,oQ?ǽ¢w`„%4åÓ|VNl[œ#xEHÓ R…“o8‚‚ûSp*ÏPûyåË|`… €„%yûár@4‚KE$€PÙ§=¬¥Êî—7ÊÜß à«ËtϾð7é(»áÙפµxv=ùô ¨ã€P¹0PcžŽ$®M<ÆFÇÓßýÝ—Óõ×,ÐO²Qèûb-ÀÂ+£‚´bkdX&<³aë/7°uÿÎ$Wp-û‰“'bEb|”ýŒ^®Á5îf³óà8Ó„ùd½´7øA¡hî\·âîüÓH€ÑUðt'9â.ê°hЋ³¹ð/~|qç¿î• *†ª©¿ôƒ°ËçÈ-GyÄ€ZÊôŒ©R•EZP¹ÌÞû…ÝŒ&¨e¸Ôrxðj yÈÇRiÉŒò;ì<”t„霚?ÃTU@dãH£ ­bñ¦ªp‚ùŠp|ºŸc;‡È@VÀëÑö—R/%ÙJRç,‚˜§˜6.âý¤Ûz¾ï÷D^dÇwpö=Ôiù@•«¨=‚9~h¦3Ãr¸¥aêÂÚ•¬ÊlN§G榾ßF ñ*ß°)`Ós-çòëçlä.lG‘õBÁwK <+äñpn›êrÖÝEꇈ£]YÝX™MN‘9cÎíé£rdá–ç“£TÔOÁ1àyb F©…‹÷oñ|¹ ÈÞ&QÈwØC¿Z[Ú@:­Á!X¾k7¬H¯mßêEd{¶3yÛ™—µx9€"Åi}·öZäãµïÔ}úHûFß«½SŒƒ"ßȫȷö­ú“íÇ¢1¼Ü6¤oªó™;_OQÖ‘t9‡óÔCœq€ƒJÊF/ãZYű–# s.…>ò*/P%áëú¤™Y_çïR–Y×ñ=ÞüdÞ˜Ö<¯¥)µp×ðmÙw‘€y§„]Na9‡e“ÒÒ ß³5<`¯£ì©Ka7" $"÷'×)"ÛLCªÞɵ¡Öª£H>¾L–?Bé2"tç$€æßäò§øŠH'¾e[X±™|”¾dQM–÷ ØN¡¤ØQHyËÔJÒ9%aQئTú6SÆ8ýSA£—#æ¤yÜëzŒÔîÞÓq@­˜DBr ñ)8“_VxG€mOâæiïÀ`À*K‚ÁZÔ˜Š`|+­ NbªY®ÔÖ ® g¡zéÕ\ÂOû’ˆ—eU "PiG æ@°}ûké^]çb9èþs¿÷{¿ÿ¯~æÓi˽÷¦ŸüɃØè¦ÛŽa°)Äú«VÅõüùsÙýåFH;CèWE¾`nE}a“0“÷œ"TGN¤ë6/a3žŽx«!€4ÔuÐk÷KWß‹ðgß±hã*rŒ<Ã̼êO£×kˆ¨–úu'‡Ò(¬óØ œÖÚ˜O¡9j(½B;xjÙ¥î8ÉL·qÐÌÙCÀ)¨±é¢»9jb§ˆvuˆ)“Dv)T—9U-pM¡ŸAÃ%á3ÃfÎ:¸€U|E•Ý.4A½~`Ί%Ë`ºœ?R\Uiùà% mBŽj3„‡ú¨r[G:¨RÔMNQЬ"T€„ѨD½P«Å8M5;W±“1ùOiËpNöàú[$Ã’2Ò ¨ÑfVh5ß9Jô+ds2ÁFTQVîå™Sìý«»É[„æoƒxÙmrU*³Sp¹‰zPŽÜþ|[PáXsÛ.rÀó‘]_ `tûÅíÀœ.ô¾6½|{¡øÒÛ{«H'¸Pí1{`kR /Ú‰‚ORàIvL•¥vqá4+øô ü§mGN¥9èã#ø ,Ôœ=‹®¾f±wº÷` X$-§Ž=ÂŒO¥U«‚Pα­óPù¦ôò+;ÒS/îI W9ÀÑØ¯áùg‹Ž´FlÇÒc¤èðÁƒQã Ïo‰Ì\×<ÇÍ›ühìdg„¥aŒöXqíûE†E|ý3G—Ö¾hÀ/"#­„âý|çP™NV‹ºàdšì â ˜ÔS¬çwy«>êSu=u5)l'^j&Eíæ¹ãIƒã/|b"•¡r†u+€r€/à¹7“I¾\f½SptŸ#ÍÆ­Ù¢^þ8Îr¨qØÌùZdäg8Ë®K]™+S¶@,µÒ|æÓ‘¶²°}0sþœ*’+9–rŒ¡™–ƒPZÂH9l § ­¼+¸‚´« |eE)­œ3×:0Åü/={J_·é•i0c0BÍg…ªé#T꘾X7{Fá9ŽýƒFR@9µ4 ˜ö3|NIÛ þ%Á%ÆôÍ ÜT! Ì5¿óÁâb2þN­Í‘Z½ƒ¤)€ì¸¹P*íwRe#Ãá¹ÇÆP!P6`8‡Eà,(¯¡ïøqvH]ÏÏi»X»¯‘EEã²è†¦ÉÁtÝz6`ÜÞ¥Ï@»pþB(ù`úû¯ÏB+ÒŠ•+Ó¿ù­ßÄqãxÚx%la#!…‘µïD= –¯>ø N.¶›wïÁ£éC÷ßb²0Iößô2ˆGüÉâq‘ö‚ÈÚ‹ov›Šæw°cìÙ.1@ÊüWi8"<®rH!°¤-ë/мLºÅ\OrHM½Î}ÈEŒ—É6,„E©3ýï•Gy~%µÿ€ú_Ñ_ ¢ÍÔÁ4¼@cˆ!¼÷lÿîâjë\CÖÁr)5o¥½D\žå% Öõ-ü’€Î†Y8᳇HkùgÊ<Öpö>÷‡M\¸´Ói[~Çø%+€ØLQfÎÓ¡··pgúWø†AÛVc›vsìç5fâ&®¾Ò“3²Õ¥œ.oÈlÆŠ@Îj¥¦ XÌ(³AíoE‰)-o ì«MSsEŠ EÊ·öóŽ…ŒÑ?4W;;Þ<–FÌ2:òXiè5O¤² Î0ˆ`å\ÔPÀ rP°`umhž?¾Îd…¨ò øì} “èô‰¨7–‡©ÆËê †‰cø‚`ÛˆLz8x7cV]y–â¯&Þ!&B˜½†z†¼À´Ç¸«¨" (ªóîk8–ñÌâ¸f@9Ó5Ü1N¹²­¾ñCˆðN/bªîÁ"D,[¯9žÒ­kSú&߯¯bFñùh¶üeÒÉÊ_õåñ™í$‚³EÓ¢µ¢Þ°ª¬Ž¬€$Ê彂0ßNhA–ø³š er%1Ε¥‘s š«’á [C¿´çLšÍ²b€~ ~(€XUàÙˆ*ÊôQÚpÉ7—>>‘Ú €½î‘m ë<¨Ö¾2ßóú‚ôo|i¡Z¶ÑÈ`9u´ ¥Ó³pty7,r©¢þÓHEeWÎUvØA+à˘ƒmãs‚B´åŸ, —S'é‡A»’àȃ÷`q14•7n"Ñ#9íù `Rv×Ó›ÿÁÚ½Y27ŸFÎXGy  ,=f¿Ô  ?Ѓdbe'囨H:ÊA½3pc:€.¾å´ÀÃü~Ë@YÁdaßÞÀ^–»‰ƒkŒqÑ”¶µÎK3ÀSÏÒ–à‚éJâ:Êwî&ÎqmcX¹ó>Âa[ªˈ¶²6´¢²¦É•“Sh”¡•˜’8œæšŸ&õÆ_a8ÐSS“±#0„JUü?9¨•+“ ‰±vÿš<70Ô0ƒ²Tgw¶XD€‹N”r2xiÐbqŽ¡­æ`rl*­š­ß@½Ö@›p¤êÏJnß¾ƒU‚b~{X^¿jfÚŠPïÔ)¸^É>B=ô sæ°Ûëí·¥ï>ü0G»qvñ9²Aå/ü\úßøÍô,R;~çžv<ã†r¹G¡ϸ‡ÈK^¼ ;:H²õÆÀ¯wa>"„@ ”›ŒpuÁ¸ÜTvàôu]Šé8ß›õÚ;Óï÷¤Éé.Èá‚¿c™%ÝÓÈ}5ËÙ@!Øé–؃Թ¨¾®'Ò…!‹ƒYÖZ¡ÚMœä@œ"P_vÔ©žÄÊoUNí`~/ù°ä‰Ž;•Ö¦‰ ü¯“NB/>‰0¦ƒC€;¨¼ß¶?2»P Áà£ÜïØ¨˜‡“»ñ–¢Üp#“ë)¿ÓDó ÊUr™Ê™~X€<Áq€2.#f3e"^kûéJšýGà“Æ?áùYœíQ.ìƒ÷¼ @?ŸAÖN£> ‰•›ZÌ!àú 9¬.λ)3+¶º-X#=⚥úJü‘95\¿P`×m`YÈ{ú‹Ž!CxžÁ p2ïÊEŒ,—¿°xD¼•`‰/;8æ}ÅlK>à¯/UÆz* í­-Õ±©FºŒí¢uÌ•åsTDŸgÝqïܽ7¸Ÿ=Ü—§s}'Cò?¦StôÈ7Ýtc,:|än©Óì9 ‰<@ŒJmÏ´!Ö¨5¨¹tI3Óm×,K¿÷_¾•V._”^Ú¶=}øƒH³ç}€1š§ø2¤aFFR2Jod?Âï>ö42‹¼ŽËg?(ôYeYK%U¼hÜE!7\ iÄ3©ÖEéêÞ‹'æ[‹»(åE¹· ) q´£ éÓ ï¨©‘rVPV®²”.@¬àESؾ"IM[—s^ñ¼ëæÇy­œ5^ÅZMöc—ã{µÊ¬@²¶ÕAÊ!\g¼Ë…AlP„eµ ‘\YP| ’zù"\¢¿déu­j1l*”)ƒðØq‡üD:1UpšÂÇJ,vbÅRµ€ª‡gT ½êä{=iRڳơa%ˆêQ¢¶|˜J}‰÷X͈œàlõ•HÆÃú —TãH(©ž2wáÔd×~S˜’”* [æ÷NOB&QÝЃDÄÐÑbF÷ðžäÇD…ôõAbDyA  ØÂ °¦‰«O÷v®ß 4~ Ï$"-­¼[6é%L\9<Ê 6 ÌI†{0úO¿ñÚ¥+àsiïþè‡1ÉÇ+Òaíûza! S$¡Ntü©zÙø“Ã-iœƒ{áÜøÒà~..Úã¹çwáÔI6ó)ü¦·¾ºìO÷lywºëΓ~õWÿ§ ò¯²í—Áa«ÌåkVÇýë€8Ç^ò7XÿpÊé7@Êè0ÎÑJ4Rq_ŸIpp v~N8}2ƒÈËô–/8Œú—ÀµCÒ·J@`+ê©\Q·! 8Ûr€x(Ñøz€óVRmâ~%™KA}•hPaQ*º€£ˆ@n lì!ç>þ5„¾¿þŸ».M] —PÙ‰„h·[P Níå ‡?Oß» @–çXYw¯ú`áÈg(p° ¸¹ªÈ*_ª/ñs|Yv>ÂQÒèM‡+ðN=«:%BÆ*ð\2}-PŸj7šÊÿ•IÅ/s}?€­~Êì–$SWàžûó!êË¿Ó ph‘* oãpÄ[§3 VÙ~d%Y.pœwàXøŽɳ©™o05ÎãÁ¼h,nÙ¼åµ!’£.†? 1)£¥½¼¬ðN@­„%{­ÄÔ1lè}ÊO¶Ø9ËÚTX\3«8Áˆ8!®%¶Ö:ƒÐíöÛnIœN³‡Ù5xaÚÆ>|j îÄéÑÇ…<{÷îKs°Ðºþ=·¥ÏéÁHÓ‚t¿ªÅ QŠjضûHZvåÆÔŠ 2}D:{v±¿ÜË[q|ùDúþåÏÄŽµ: 9s¦7=ñüVÞiÝ•5@ÕÊŸãê~ƒ€#u Ô=Ž2Eê€9ž×çç{A jc¬–yÆ·‹ ëó¯¿x~‰³(x+½©ž.d/W ›¸*·Q^YzæÍl²±…€'õ,Bfg³¬IêêóZ©(o$_º tñ^Pÿ•®sB çá8hû8ˆú jñu^=WäëÙ~Pp=ÓQ†õ\ûá‡È^YXƒš ôô§†7:Øl8BAy“cˆè|ùöß ˆ¯1&5XBw?rªµcŒÊdµ!,”ffA†µ˜!állžÁÿ\°øy½¾Zô§öy« PßÇa…ii°°*AU† ÙžÊ0ÈöÏ‹Ölb¡O.¶þÔÆF3p°âO¤Q¸;…Ùð»6u²Ç‚X[ð3sF+‹éégŸcˆ•ÒÇÑÜ}ÏÖ't…ÌaSD¥²½åÏ·L”>ZJ/E;gå‰S>Çà7°=Rmóåðu]\±-ÖÄnr~Šh³ó•é z£^\ P °ä‘Ðï T>³Õ¹S6k¦Æ—i¶Cœ¡¬ìò›&WÒ†÷ð=9¦1Õ•œ3™-÷rÏT Öo Øï»9D,´Ñ• ­Ö*n„>,£ašjXž&ð8¹Í¶aÌ’6¹$¢’ÿÃ\›‹ã¦üí\wG| 3ìT†˜i-8ò©²½YããcŒUvk޾‡wxâSYËEs×@Øÿó1 mçM¾cç]v Êï$Ø"1z¤dð€AùÂ_%!Øäám…ηR!¨ã%êºë®OO<ùRýž´ôÚÅiýº°év2Ã`mi9àî+¾BÜþýûÒ•‹ØyHLK×ó¼çEP¸¨Ý€ñcGÓþãcé¾woI=þD,tu°kM­ïÏò®2 ÁÃßýnzá٧ؾlAúäO~:Ý|ó-8*9œ¾ýíÄVá\,@rš‘çiµ/Ö€°Pÿ õÀîuPÈ¢€µ³é"X±x)Ÿ¢¢93:[Kžß᥺gõ.¼¶å•$ÃIÁrNžaû²@Ä ÊÒC ^)¼€±Š´ë8ßÀ!õµM5ÀÙÊY)ýΩ¡è .Á “OÂÝÊà½øÐ«p4nn^\7á¼åN{;•:[{uºòæ% ðŽa„bHØàŠtº8DZ«IÊ‹MFF¨õöPÐõÿæQ]Ƚm£3¹*Šõ“{ÌD`®slã°Np¼¢åòÂä¦Cð:›á*`­ôÆîIš‹%”üœys‘ljØÇamÉ·´”ϯàûl*ª VÁ!I¹åxjž…á8j[W-X:+u°v”içàÝâõzœq}eõð9u˜ OÖ4\6™%ÃÛj©apdŠÅ¯Åà¿' T5åÏIé_šÀ¢©¶_j4Áª-×L©r"hÊû (¯(bÖ¬9l^1”þݯ|"}ùáWâ¹[„¹T]»ƒBºÞáfœ[Ú¹tì|u >÷ÇŽ *?“aÀè¹iž~þî£L9 W„¹íh$ä ¾ánÃ͈9žžÙºoE[Óšµ›ÒŸüɧGy„Œ_HßøÆ7øUŠH‚í |õÀ@ZC10#%㣼öZ1¬Õ"Ä•ï¦ÙÀÚÃúüë¯k/yR 4ÅœV­k[+ÔW`¥X¥·’ô v¯|‚zøìH–YÊì fÑP m;ûÀt–Í3¥†U5@+Í § <ÊÊØåÅÇSûÇ—¥†gˆ{‘WÎQ»¢MÈ'{êy”ì¾Ct/#‰þ¬ÞFÎ[H¼ŠÃïÚRmõéîýäáýgƉ–48Bø˜*̧™ TBm¹”÷œ¶8} -Ö€ù ˆtÌÛÛº2 ¯·ÎÚP¾߇ŸnXz[¢‘]}1™yµ·hC,üJ±Ò2ã ûýjUŽewjDÐÚÚqíØ Hí,SŸŸÚ;ZÓ 6Vhj¡G€…NTÖs@g,c8›1gà§ÂF_š ;eÃ! ¤ˆÒùQünŽQ²ÑÉ¡Ê${ªhÑbðg£ßrx‡€ßÉ,† Ç0¦°µû·à­-3˜ Ó!Ì©¸E@·ùšRÿ‰Ö´}Ç®ôõ¯} vûݨùf§'žÞ–>úÑßN§þâ¡(ü8ŠÌêè -€p™ç1·\›oh˜ÑYïÁ¸nmiJÏãökㆫÂC°‘ßÿþ³é•W³ðWK̵ ­8Æ3\‡³Pʪ:r||$ â~lÖŒq| .L/8ž~í×~%­Y³>ýÎïüNúä'?™þìÏþ,}NAÙ„'O &â|1@ÚÁ³Õ€™Ûé0ý,bÄý´¢éjS¥èI‘‹ \F\¾»àºuÉSd uSS2 ¤ ¯`P­|Á²Ë|J*´œk»Qq¥æÎÿÀá´`5i6sÀ£Ð\2µpQê}ªûoëD‘»)Y‰¦*¯ ùk¤5DE|™Òòª¼hvÕ­.!޹r ÙiË';îÔ„Šx`Uª@9€42„;¨ât3U®â^g "«ƒä)§h¹ý–S…›9_ÍA¡jåW„mu[´1µÞK_LìMSÌÍaÏwã»ç;¼yäN35¸ŠH™ˆž¸dJ4?.—ŸÁb¶Þ ¥ŸÛ’Úfáܦ©“=.ÝtââˆÂ?§¾øÍdìiëBž ËŽ9sáfc°5 çËÇsõè9 «\áœõ:‡" ‚ªHHÀ¾c¾AõÿûM¢+)HœöR0{­ àgYp«öÊ´r€/Ød*ßÇ»wÞqWºjãæôM¨êìµ8äå×ö¥OÿÔ¿LÛ÷#Á`Jsñü£PªÛŒ‰pXr´mÀW‚: ãhí‰k»Â-ÀÝ ¼p)¾kÿá°>ÄOA- 3ÍYÌßbŽF¿“·åÓ%ù{¸öîWw¤[n¿%½rpwúÖCÿˆAÑ_Býÿ!}úÓŸNŸýìgÓvÔ†ôÿþ?i{]°õõ0µ5t¦cª>Ø0ÓqôE!bø)ÎÓ‰¢‹‚ùÔPE$+¬/JvÁmàà$\’Î-*gx÷iìÉ^Ë è+É[vÙÁ-ð p®r{Œó·¼¾šã&®&Bh£¹ƒp{ßb aÇqv!m—ÆS`úPÌ_׉ïMy?ñZòêæÃ\{ÞÇYZÍf!¦#Hðà ~týÕ)Êô„¯ýú¿U ÄЋBú/2™E~Èb]õ\Äá…ÔÇnœ¨.LjšøãéU®73^]¹`â°.M‚?šØ6lV@B†ûKòìN-m/ðOá(d0Íìèg°±ºN­H»û["(D+Ò€lY_ L'Ê,rÌIõmj½OáBƒž¡°mø©ha ·!;”Ξ° 1úÑ&åR¤¡…ýgoµÈ¥èî.#ðåwjÀäÄ9ƒôaÆØ…PƒµÕ2ãÆÆìã$ÐÐÜÎ9þúsϧûï¿/}üãWš™{ì±Ø/à9XsHÈù|±¼Wç"³:ºÂèŽý½©u¦TBÐJèZ ±J 5†æøë®œL`>ˆ Wµ 6¬G#‹í2•DÏÂÇí”b˜pùöÂÄ1Z¬²ÌT*Ù ,XÀ ˜¶ÿ”ܹ,†þ4 …0ù`Nÿ5ØÿµkצÛo¿=6uÝÀpŸ+÷œéà#obîø|ò“ŸJ_þÒ1šd/?;™šcÂ|(®ý‘ãq–‚ƒÌ­†‘àÎêÄs·öGøÈ+^’wg#íÈŽ¢=X¿¢Íòô8¶¤Â?Õ3?þxP~ü}ÈV¥oûÓô™_ü×é³ÿé³é‰÷‡†!O@Õ ÛƒuÁŽŒ˜@å´šé¸ÝoîæºÔ—¾4j)ü,¤ÛzL2×ărÃLÔVx¡)M@±ÂÖ|%‰l{(­ ®ä EaZ0æ+ruÜQvëpJ?ܘ·íâÑI¦- ü@“5¬¤ëüuú. ý=ÇÁoA‰òeaã^®EBHË¢ r!–cmù”¶ïEÂNÉщîELæcùÀB¼Ÿ9åcNs6Ó®7pÞÏ!r[Å!¼¨ÆS’ŸÛ^½ZÛO•R¾Ë|{"T‘øì^žZÿ~Éi¸§µ©75ÌÚ…%)ªÎl'Ø\N¡©u„d_LHÂ;§”%‘ƒ­A £¾0ä4$xÈbä¦оÞæÿƒŒ1‰DŒq&W nˆûèI¨&6€U-MNäå÷\ÖF5Ï/3¼Ó l}½Ôk HŒ€xe¼Ð4à…F}zˆ%Y4C¹µ سgOúò—¿{ö}êSÿ5ÜÍ?`±ÈÄÆ(ôëçÒ-7ßœ–.[–v‚7!X™ÑÆ"#0åë $à;ííR.„†ìôê®c±Éˆ}m蜅™‰ká(‚ÃÅ‹u/6†fVêeÃÑ™3›Óä nkã‹´Å;¿õ›ìfôו–³ˆéßýÆÏ`ÒžþãÿþÛ”‡y¶lqÔŸüku(Þ+¾çÙ¸8ê"¥ÌÀçÞx]„º2ûüÍBæLÅÀÆåT;ƒ³€¬`N[š€ž\Á³ÍRU)ðnŽ}ž‹gÐêê4½Àñ4Ç3/óÌ´‘uUü•jšñ¬µëòyê]Ce€="ûÓØV†ÿ÷ˆ¯¦‹n¿í%ÏosÆ\7i•x×K9TAÞÍyG¬?c&’SNE˜’4°Ì·ÜM¼ùÜnǵgr.æÁÐ.?Kÿü!2¿åÙKÄY‡ñL‚MÍ z µmEYÂøõÄü_©ïûW¥;®¨¤yp0ó&S[çq¸„„©˜ C¤š;µÏ$#ú£uH‘õ ®ã×±‡åÒùŠÄLÍ—Þ©ûbØÛ^Û¶+½úÒ®´wסÔËF¹ÿèWuX’ŸítS‘[¾É£©2YhB áÓË VÖI5°Ÿ7“&-t(ýó11–ƒ°Œ`…iL(†œ„ªÊ~+…W°&2¸ÿþûÓ5ìaÿÐC!ÅÇ,˜^jeNÔÃN>Gê_¹f çhúÒGävö¼3_ØÀZøåºØmÈ郂–fÿÆUsÙØ2'ª¶Î¢#òhÔÞÿÀþýéÃþPÚòî-ä…ô•r?ûâ:™Ñ-»À¿UPò?»£oÎKŸúôgÓésé÷þð¿¥Ïåëéž;ïÀÌ[ôÎG¢¾|‹öx«!w«é~O’(ò1ßÚu `þ¤õÒMÔÔÜ™FÆ1FPL¨jÌýmÙ)ê:>-å¥@U© p¦Vµx€gp jh*ƒ?ªl:2^h¹›x½æà€4‚Å…"‡½šð+LD¨Õ3¦šûÞO¬*Ç#œ] ,å^ÅaÙ öL <æ½ööP'Êã¼_sãÉž÷rX~„Ì¥³Àp'e$mô ÆA•`Õ9Üÿ@wÜêå}OgÎÚJúØ/•Ó¯­#ßêOþÿhzeÇ‚tbËŠæ ôƒ¡˜áz‚¶ã „Ì7Á⇖]kKoXî‰éB%µ##çØm¹?•C¡J–ÈÈÚKôf€^@q ʸ¶õ Å9·&¿ÑÿF›¾pV ŽüÎåü¾#`!ssé&&FN*º‘mlj¹P`´Hh%üœ,’ #±¶T^)is,çð?ñÀéCü{ì —D | ND³;™“ïHïÇKðÜÙ¸x<|0ê]¬P²ßŠ¡ÐL¼‹úÎMWoÞHc‘Îm±NÃv´38‰ÃPwúq,ÿ÷ÿsÄŸî9•V.YB_çqy¦ÒÛ…‹—±va‚L 0í¿ï@Oê|"­»J tn¢]<¿.`f'~î[gIù½Ü¢ÓÏÈ bêú:î£ÉsÚ×}ÃœŠƒÄ¦XHS9VÆ”É.1/v+ø+ÉRQÔjÙrNÙRÍ} ÔÓMU)=º@|É‹ Ò$KdÇŽ Ú½—GÁI(i§A„“l<Ú{‚•„6ÕYÒK¨#ôä4á<ʱ‚c €cü®äÚ²ìçŒ€È A[u ;N_BF¡š!ÄPý¤ÓÈý™5 ÚˆDt²quokG×¹<-ÆÅÜ=Ié#?ÚnºQª+ÿ`:udAÚúÔXšÙß’ÎP],GЃ ¸½EvÓj§p±p®Ú›Dm7YØ{lŸ —s##L Hö¥ÄLaq™)]A™é—(7Ÿ´/ á’¾>ø<Æ¢óëäÇp©ÄùÉ[ü}GàâoL17‘v±¤±4¡ rkïÜ„I¨.•4{l‚e.+•wœÕ{ D#ôöõ²"¯œ~òÉ´výÚtíµ×õw~n^6ælŒ(vïíN¿úK?¥>Àη#th{°X"9 1l3GX÷±á´áZ¤Êºx"Tq“} ƒê¼!3=Ø(s˜˜ÈHâ\owZsçµ”a ßÕË‘N4n¸z}ÚºíHêпA ïäôìŒnñ"x®ï;¿è`;ÝÀm|-mŽÎwu;8Ï.A¼œ3˜¾üA~É®À†°ûÇ z_êÅ8Ç¥¦AÅ£QA•ed£Á ”2æàÈ ‚¢~˜Ân$ÎÒ8j=fo¡ö¥¹ü hLÁÇb8JË™4tpC;{Aþœ4¾“Áþ4éá’CÆ•# Ô¾š~Œ³\È&Ó8ïW-)8Äq˜kÙúÙ|š~k< [¿'åÀÂ…‘ƒÁg¬Ì¨漟ҪvCñ9çàsçàcr]ZfA`Gcôã7ŵ‹ššþl†kÐ4•tß»îÇðæ‹éÁ¯<˜n»õ¶ôþ| i±·{÷n„(èjÁ¸C÷ÿø‹¿Ež9§,‹}݇gù‰tÍæõPù9`æA:¤=éc¾J©qƒ¡Ï s´4 †­/u§[îb´Z"Šï‡~âΛaƒu}Mhf‘Q1ûWA+×6ðcÍ òåtœu/[x0u­k)óiV.ˆ}ó›"O¶4²bÂ>:ŽdÛó|—§£+Ú›øªÇdhŸhÈ"Õ&`>ç•¢T¹n¨DÅ(o?C¹é6¶+V%àäÓT‚ã€Ú¶w¼šºîN¥9ìlñK>|»h˜0ÈQ5ù Ç.¢¥Ü˸†s@e‡}!÷k¹ÞÇyïíåuÀÄ ®—G±ÌíØOû&ni®NÚ1kG'ßœæt\‘fÎ[’:¯LóVÌàžZ_ BX@v³˜—· Qêí›—öó™‡éÛ~¨7[¨E/_.÷…z¯ÄêÂÖáþtn¨!ÞÞtnc³‘žåµýnTãa[¼Æ{-°;Ü£þžŠ0Ý>œ¾)žæs HHÆÑ*œðÙ焤¿'¢RœÌQ(‡ÐÔ÷ð!v!FÈhØuàH¸×á0Þ‹ò"À sqêÞ»+ÒXÍéÀMÜR¿?Ù§ä‹Hã‹÷šï‰i > 1ßÖbó ¿ßO?¸ÔEíSÂQ³µNh|t BÀ»Èk @¦t};o øÎ§eÍ“ïç P‡@ng»ˆáI}˜zÄÞm ¬·sî@ʲȨVA !PÝ¥p_ÜðÇ|œ0§‡ºWÓpV&à”DÀg>Ž-‚ir:2¨n¡ìpPÁÌÚÛ°õå3 ¶!ØúL¾áhZ¤9ȪS3@Þsà¡ Z‘wµ±åDZ¸¢œævaRÎ넉: ð3+Üul2<6‘ޱÈøY™x§b7¢rép:Û»?èÛÇËû1Sh©ÀˆصvÕ‡hr u¬}îêº6³ÿ/jC"~p0“ ˆ§´w¾CˆÁq{?ïDwæ‚Ð7#§P@2Jí”· ‹ |ñ T!†€{îÜxúèûîí.O¿ûŸÿ?7¥Õë×¥ž§Â2ïªËÒÚ‚xƳ¸ž÷Ýw_Ú°aC¶Ûøá~%§îÎkÞEÐpHc },[º$|ÿíß¿;]»îŠô cmÙây©o€"2°æÒ{݉§bCïé´béÌ0×>zD?fwÎM·MÀ€ Ì—yžÐà Ìä5ÝåyäqŸ“ÍUÜÅ}]Ó^"„`dϧ©]ùž¯ñë’äŒÊc+4òÌVl“H×G‹òÁ:ðYªEÒÉÀË¥Ïþë•ægk=ÓÀ^_ ì«›jàñ K燭?²Éän-‡jû¢ìç³q¾¾¼v+ Ë 8¶a·D(ô¬³çËiñ‚-àëD³I|K{+{ìMbÓŒ‹÷9¬&ÅÞ£¡ƒñÕ(kZŽšw.šžVÒ”ÒÜ%Mi8¢‰>bÓ2®Å÷æ[øJèÁáÌÔhkj`³Ø£ûÒ‘ÓÏ¥Þ³ÛÔ„Kè/«¿gΞJ#ð  ¯q¯â/εŠåÓt‡Õ·‚‘E›]ºþ¦Hº‰)@?ãKlm¨Ì|s9¿ïpG¢ £C}ºƒ§oG BÁÛ‘=áia›~ä²u]xæ]{ýM)&¤wßzWzjýôK4øÈ±4{å¨K=ìÛG‡a?TZ¢GËÆC!\‹(ÝïÉŽ{îD­'a­0¿Û´qyþCÚ´¦=õôf6^™‚ï܇ ;­–N¡%xþÙí,:êLÇE”Qç°+ÏMûºóôKÄS¿@Oó’-žXYƒT\{_»6}¦¯b,ÔîLgŽˆ&q<ñ¾xVdp©s|ž³‘½íQR ™+áºK €ÞÅ›Ru·¯)AÂå0»yõ rº\@IDATñ×p-k.2¨ÔU˜_„H ¥J£réÒ®•ò¢tö™ÙiìA>¹6YDMгí'Ëv€ÛȪ»UÔLK ª ¡ú ÏàÉK'Á-ô†ZÍ-©¥£œš™Ó· Yb7Jö–`ZYžb:f5¥V¸Î,MŸÙ87 –ÛY|3+-šCX–Û2ÃûS/Õ™‹Õ^ÿHràä`zîÕ½ißÞçY¶#êÙÁØ„ÒKåÙOÁu#çLåm?ÊÏôE4s4xî$/‹N-®k#>âLÀEÄó“;—¸‹|&“Ik6šRSîùAÈÍå„w„ê>Åç~˜ö ã_p1T-<ÊÀ‘‘R#B3ר71w>Ê*»UèÍ››Û™G§ݸ%m\~U €;ßO:x\j`¿Ùæ†оóï"¸óÎ;Ó5W_KyOŸ>Ü…­ÑŽ"Œ4°ÔrÖŒ–tUÕ‹VàX„MAǧÒBEZ¾Ýa¸‰5¶_ÂÁã=i~mÕ¡K4Û:Ù;p‘I.›”¨…ìáÀ,ÊnAêCo\qmÿÛ•Eukñ¾iT÷äW_n¿q gbìׯ"*jÌ™Ø^+2’ÕŸàPB¾”óZDĈ€-Ìr}¦Nç4ÏE"KÂËñ>×,¡mè€óŽ9¨}«jzÊÊbØ÷nRò¥HÈeó°çúú²ãW$# ²o¸…,1xGŠ^‘U¨>ÆèÞžýXjîF€×‚¿Q€Ý܆r±D?̆{,ã$¦#ì‚f6Äp<ÓÁîP£c XÜÀ¿µBíi!{~O17_¾€¶Ã4·4Þ {?œ^Ù×—víÙ ’ g4¯¥#§NÒXX"\jjD–ÑØI]¥òúµ´¿kÈ—ÒZ‹üãE]ˆÜgÕ_G‘´at® âÂÎßñ,;Bô#È6|¨|Œš×¿›Ÿ¿µßwˆÎ“Ï)¡œ¤¡N°ÙÆF/áÿÃ4$€Q©¥ kK#å´iÃ:|÷¯Æï«ióÆ©ûoð¶=öiySfÅ'å Ð²ÃªgAÑ Ú tww§;î¸#}êSŸJ/¼ðBz‡ŸÃCu †š÷žaÇà[¯_™®¾jczéµ^6{Tfàj+;Õd(²Ž@¸›c@KKczß{nN>ñ4™â®c.ˆg$õõ$Ì{ë8HÝXt‰g9a;… B´š `6â‚vŒx iãçâç矼áUdð»£MS×Hê„ý=~¬ –¹+¾éèjºŽ3Òõ(aøÚòCÒQ¿¹˜&§qJfëkÁ­œ:‰“aÂ)¨èäù0vû èÇ«g›²Cé•ÂGò)¹ÈA!lw•Wˆ×ú­¶~£š©Ö¾¾µEÁ"; «>pGFÙVŽNZ»Ð&¡ihš™Úg7§Vú£Ô1;Ílƒ:ã±[ÿJZ¶[æYhŠZS/›“ŽŸ|%½vøy r^BØ÷&ß=X—R \×·;ehè¢éå Ýð„FÉ2A‰Þäîm†èßáý¸öœo§ã"âò%1íÆ¨†(Ne# †N8âáì3ÇË ïÄ7-Œ¤oÀ#V F|ª\jD[¢»lXmŠ_V: F=…Ýó¦M7¥ûn¿/½Ú½3-_¿ Ýrý ©Ÿý/íÜŸ¶l¾cžÙëú€)Œ†çöšIìüá‡í€ò7†y±þt ¢ÙqF3ÓîÃiãU+Ò_þùgÓ—þî©h­fÊ56æ|2² ÎAÇ¢"•¿ù›/ *ìI —¬‰‡oÌÇþ`ïA¦Çzr9EÚ5ö€{‰0 ü~è=•¯î]ó1-G‘§õ-®/ñ‰KFÅçxb{7ÀVŸh…õ`»>¡aM U[LZ©½£h=§~{å$N ¬ç!Žå–‹“Å•þÈU0}n»Çè…ô_©<7M€pDe§ Ìœ,‹,´[“é'O|3mÙ†ççÖöã°øØz4kN˦­HûÝÜtj’ÀAhbë’íÉ1ÔsÜkàÕŒ½‡2¦6¦3ãâ´0oÆ÷áÂNËåÎ4‡åÁS˜Ün=ôr:{ú…tòØkiÏñ}p§S ìÕŽFª+Íi£Èp§Nõªµ­¸‚Æ3¤­n®p\¼½ŸèÖœC¼X´]qކôÉtÄ¥óÏ#7Tí²N„¢‡óÝåþþ0@|›âFBת„í4»>ŒK˜Z¿F„2.ñmeÅÓÐàh:Œtþƒ|(uŸ=Ì*¿#¬‰Æe5lâàà6™ÅøC«2B!/X{ã €!(øÜç>—n½õÖô‰O|" ŠvîÜX|ÕªUpχÀã=ž~ù—?“^|ö³fAȶ,ÊÉ“'ÓûÞ÷>ü<—vì:’>ñÑM9)¿‹¯˜ËÂ#‚ºÀ"b±œ»€n‰ò»àHY÷ó:QÔu ú*y dQ—Û›^:ZD—ãÌP¿µ6Ñø¤sáR¬«-½-…Þ”"kTäÙõp k*ˆ< 5ES÷ ¦ñ•[0nãi&¢?µ®gA×}íiøAr#SØAh9ÜTk ºNBå‡ñTt cÊÆsY톆 HH Ù/㔀ïœéÔ€7áfa¦=§“éÅÓ±sì¹`,±DD²ÏØu0 = 2~È^=ój:pøPê=‚P˜-¹x¿ *ß>c.㊒ò÷ÞC‰"qÚ(ØÛ[Ðãm†â•܈¼Ë™XÉ ‚÷~GŽHD)×ïžONç¨âCç\ÎÕ äÂÂbžœ‚•  ÙllÐ5þaP¸Uµn¼gbR»sûÑÔ½mã$ÅXçÝpóøV§ÆYíiÝ?{_šó¿±-3†AõS.ÚQ‹`P`õ.Î.ÔÑVàÝï~wºçž{1ø¼uйsgYÍ·5=úo¤—·KÍÌ/¿[ä%‚ш¨µ¥%ÍÁ ©¬þ5k[Ó‚ùlDrz€CWcQ§4<#€¸¨ûqÝöµŽ®ï"¯}^t¹¯æëZªúñˆ×! ¼µPd'VÅC®KR±Ïä£ðëU©| i?ïa4ÀÑJPÀGXÒøÄ]ɵBÙúÚä[ Ƕs¼pßJÛФ%)ò¯²¯Þ0Òõ‘ÿ‚WâÑrjíÐ.m]L ÚAX—XÊêX·ñbn‹Œ"Kø›@° ìÊX×ÍDØ×PnÃsrà ›ÄŽ·ÿ4±½\ï˜ï"¤m|.½ºãD:zr?~$16ØnC®¡ádK;õÃÒ/Syí(³@Ï¥ÑÖ¹†ÕÓgo;­Ï8-9VA*1ýá2€»xnÆ^Û~Wa¶ iÔQÒÀLL‡k~ QpÎî‡ô »2\ר6/,­‘ À57Î ¼î†«Óçþâ›é¡o?œþŽ¿˜V,_†e_ok7á*¼¡¥œîx×»ÒÃßûn €@´WAµ‹" èJó~éK_ÂÕØu±tW€ö{Û^y%m}þy¨ z}æú†yyF$³3à£Ç¥mûpBºàŠ@6­bð! "D•â*ÿ˜GȨŸPvi}(zª8çgÅ]Ñùµ7j@_<5¶(c¤xHÁ…ªËe²ØÎWç’ÓÚ´š3ƒ/sßœÏD>)srJ°ŒXïs®æ‹`<µ/G’G?6hÂ2NÄR‘m¿3-|\‘°·mæ ¸[T#óöŽ1&°ø,±šÓéa{;ïÁÖO º-7â9Š~/3m))døLŒ°iG´¿þ-X‚6³èìDØ›N÷i{¶g²ÇÂ\ÛqàŠÒ&Ì.ÆÁwc/ÛêÛr”½åv¥=ëÛšèË ähß  Œ vT¢m€r¼iŠìá–L.À«>ªrl)ƒàªHÈ€âÖ@x¢½˜LE6™ ©.ó,ºùa…(2€cÖ vª¬ík¢5^ Vt,Ž"'&GÒ0UöÀC¯a7ÙŽòŒ4„éÀ »^H׬M›n¸å«eæôëÓྑôÞû·¤ï|÷áé³s•\®—Ï:ùt¸Ë²Ê í采eë¶7œs©-š»ÈÝó„½ûÊkí©‡Ÿ³[ï¸ÍFÇjm27pûí·»ò{ÕÕÕö‡?üÁ‹"a¢†OÏŽ¸' ÓÍŽ'ënF;p±€Ä¬z~¥}ç;ð»„(™‚‹˜ÚíÍ÷øDTH i bâ2zÐ5Áè•?r£÷üE©”OõôÜyC GÿÊÆ·Â øœ“Î)Áñ)W¬¾€]ܤ$þº×Ö\PLF¶ètxå¸Us?Ïùƒ¸Ö¥L^¦¬³óK`θ:Ínø{ÃxùAì”t&îÛ—°¼µÀlázÀöénÇ“Þpšxø¿ =â¤OO£ò2»ðZD©xÜH¥¢c#iìñÔp í]‡˜/ÍoFß¾¯“~PIÇ’2åFpZ¢mG´Eh¨[èf­¥yLôbRo*ò%/¼êÛÐ;{=Àcù$ #P^g)ÝW8²àÇr Uæ›`CÒDÕRYHJBý;eˆõçƒhrV‡$$„TÌ‹‰iâ_Üïl ¯?¸o‹Ï.ˆÇÇÓÒó!Êþh¸¦ûëï®6 +Zk]¿ëP³-<vÃí6vè ]tÅeÖžÛo¥KVÚéKN²[í6& [EPˆd@=øÔüèY×-°ÿµ‡jíìsζ÷¼ç=öøãÛÆìÐo|¼ê¦ .A,)| úvçC»lñ¢ «,/°uëçÛAê¥0¡o©•{<#Ò軉ÔQ¦hö­ÏØÕ×\íÛ=O=½ÉÙõhg <7*¢óÂÒ# ½ëîévÚ¸ä’KlåÊ•öØc¹>*!É ´ SâEÕÕlî³k¯:ÍÎ=ë 4wcø¡µ°†#šþè?:59$#f_$½Œ†5f½š¸§¾žW@Ý'å}q·”ª2ðwZ,i ^mƇØ÷Òy{(ë¤HëÎ p˜Èã[I‡}kþq¨>ÞšÇÙš)A€W^ÁÁó9›_¼:œ˜½†Ã4P–‰Â¶§UsWŽÞŸjµ5ÃS ú/NFpH0Šƒt¤ù±A` 0Ç–_[ycèü·ô`n;VkmÝuHæÛ ÷ùì–K­[ÇÚv¥bü¥ÂVµ+‡dQïE××KG¦VÙ Ó€^»!zìýýŒýh 0}.m?ê|^-Iä0‹[QqY¢ì<ÑÎ+«º§<é¤ÐºÖ_ú­~Ôh5O“[väT…ý¯Cª.M¦®r Ö”2žº . ³@Ò’Œ‚ÍS`ÝRÁ ‡[\z[\Rhm#½Ö @8úX¼h‰µ õYù\2>7f] »-õÜË.»œ­¾“`¸M@ÊA2 :áÇ7>îkû´Â:»‡q F×ßýö·¸ü^n7Üpƒ=ôÐCîî;J+)Q´Ÿ´c'BÅ_üÉnþÙì+ßø!ˆƒ¶ < O§&¢&§2’§`4üE"ß{:¥LLÝäLÊ )&ËTyÓ’óÂA_ÎÒòŒŒ!æM|_ÕÚZFZ LÊ^Ö ²Äk©"d›SÈ]‡°s†B}¼ÚªJZø"¼QƆÀ‚j³ê”ˆ‹Xܱ–׺7Þ lYG#Tÿ“!n .!WJN'”¼Ÿå£— '€¥]÷Ðôv#¨gO?xô©d0Ú†KMggŸ† Ø©=ó„6ò™@åÕÞäžx±=¢ü3•©@gMò ¾¦'Z”;ÖÞÁޜΟ€þ<€~ñª‡ÊP~æ<”?ȸhý¥¥m™Dî‚~­ýã¼b~ÄÐtTþ1°xY^%‡·úƒªç…ÞQØTLâ_üïlqшˆ·AP1ZKK?m—Ñ´Œ¬´aȳo4TB’ØQ„6;jê¬4Wšz‡ñôÓ‚çžeöÔágpZcyHãÙ8´]ÏmÃT¼ÀNYBº­­žç­ûWRT†aѶï~$õÍöÙ›n²¯~ýßíÐÁC¶téR“ý€‚\„ °?ð¾¿Ä¸Ý¾ð¯_ôí¾eË–Ù{ßû^G»víò4ò`$.@š——c¸{ƒýGVºmzô/K*NÉ!jøä²@£©¿è×oyl|â‰K”3܆¥÷>¸Pwâ1™!QjRÄqÜ:…‘Ò ëû^ùÞçˆjgšeå†l&+«ÄµêŠ VXfa†å£ ’^бÍ›3¶ ϵ¬¢1+œ—beÔ©,Ǭ:Ö¹–vRÓñ¹8Ú˶]ëÛÍ»Ëõ5çû¶K 'ËŠ3‡­m´•í·C [¬cø94ïjàXЗ)Lz»•‰>¿#b¶Í¤z­^’±ó˔ևþ=Žæ#‰ÊWÐ Ë$¬ÐëúÔ6¶Q–Gýçðw>@Å{qU~¹ò 6%P¡&`NìÀN{±D»¾ÆX;,ÔâpÂþ¿ò%Ú¨‹s×Ò>KZë¸"1M’&‘Þ¼ø0[ |Y j†ºbcšf#,ø´~‹éF9(B’ÎQœT¦b:<C ð-*­´†~ëÀÙc *!•Û~m!í_R½ØÈ¼,«}² |ÔÞñ¦·ÛU×¾¼¿´µk×Ú{®¸v~£ýþîÛ­/­«,wpÊÉ'O ߊ¥†åÃ…–G…>\Œÿá°ys«8àj{c$-2Yç–••ú.‚tÛìâó/±ÇŸa9BÁö_!â(üŸI01tÑ«‰kbH'ž'nxá㜈ðiI?*îe0¥Œ1!XVeA}3òÛ,‡=ú¬œ bÊØ-Ê£0‚º-Zqì—ga‘™SXdÅ,ºYÇ”Ƭ‚ü#… S–àìÞÙ4óF;ql‰ ˜68‹x_ªµ÷r¢sB^æñH_—ímÞd}-ÛÁÆgÖ"•ç(,tAdá—Š‘Ž‚OzHýç€î,¶Øz«~7{?QR®=ÏZw*ÂWäo@‚<…Q–ö^Ð_Öõq؟ĨH?Ð#a!JÔ<zÏ~¯À²Kp1ÎD”æbŠï¨½z/¡ Û¡´?ô…Ú›hsââTÚ•Ô‰~ùaVøL û¹}MÙ¹¬9(T’YÑ>yÕñQå*Ì®£—ØÚ¥ ì‰}ÏZ}[“UÌ/·§·×Z'Ž9/ZkããL*:ÒqÏÜÖkO?ó¤]wùÕöšS²àq榿ýkËØßeu‡ê¼'N!ÏÖêuvׯûX«!s@ }`ahBÑÊJ!…t†Ø‘Ðâ)¶ ¶óÏ?ßì $× ™€ÂÆÍû10­#­Û}½¥¡™“àëÙfø™ZR4-§\é§)ˆ&ög(lÆ(•5@e¢^;–†±Lj©ÍG×"–±L:¬þ0ˆ}ÿœLìæsóÒ"w ‘Ž«íl†ŠòqzɈæCõ«p¿†!ž ¢ÇÁ–_ @Jÿáφ8w ‹=üÍÛjmûžÇm[Ý“VS»™þ«Ñ£À…ìGÖtèà è%æÛtêz§òªí+¢^å‹@OÅè˪u:s4Ž#’¾KÀPùsxfíjÊ̦u?PÀ›Æ.S% Y©þúVÔàT¬>ÑS8¢]å„íHf¤Sû^»c4ÞЉï)V½DB “Z€©/7Ì* 2^1W¦p Óž¬Ì(¥‚*o@ø `¸Ážh{•–”³fïÆP%E4J­^{¢mb»²¦×a sØêkj·Ö|„X#¹ÖZÓŒv_7’ùeÖÞÔb¯ÝŸÙ3;7Ûžç7ÛâUK|œN>éD[óÄÛºu+ÛE`iFÇÓ­jñ|ï3©%GAÇÝrË-®@tÚi§92Ɇ‡Ø«f&ÖýQƤëL2€Dw*šIf¸="•¾Iº‰Ñ´‚Ž381%÷êØÙx·‰ã #[κì÷®21»‡‹­²A>+8m>ºùùèbè Ö‚R¤ùY Ð,&”2µ‡So0œC)`p°ÓÝQcŸ ÀÄvÔîfËj'ù §@ÇÐñ˜#$œÞaÑA{­€&Û{üm:Φ“,ê9ÊЊåu Ë.ö^kzæ†ýëkzˆË(ÒzïuÊð¼bÍ5‡ÄÞk'‹bøéEÑ@ÎËtyçgzPØÚn欮ђ&N.!­ûÅ4ÃOøÎ DÝáåzKø0Ÿ˜p:9%¦ñÅ>¿"€%ËaU…™˜Î;“¸Â1ã –IóxXÆ=Xo°¾^TRfÅœÎÓ›ïU‹[íÁ:ËÌÍÄkKºüÏÙeëó15—)n?.Ä[ì²K^kÛnÝjOÜór!p¼½/©´–'´·Íjlø°]qa0 rWà·#Pum%ÊüWAì§A‡Ô××»]Œ‹|ðAÖ¢´abd÷­Xxá¼jî|;4Ôi­l5×·Ú‚Ò¹v¨½Í†zغE«ìƒïŸ-9¡ÁÓŽ”bm¸ÂFñ¨IùŸ¾ë!{ÿ;n¹da*ÚmesË'ºOi¼2ÒA BëׯGH–å'i«0`ê©}¯aTp€z*øXúdòG~¢TÑsRÌ´WŽ@å(udäÉ’â'Kšz§ok"y-4É0¹MMEx¹ }yy¨ecHÃ4ÏÁ)FŽea#?ž–‹êÏ+”6]¦¸Ù8ÀÀaF_7ÎÁFj84õqÛ¼÷q¬éž&®¿ 2·F^€®}In‰¸=8 ôô‘õ(¸×TÞkÇÛäF'ß{Öñ“\žÊ Ò‹` èXzq%<.dMfú|ŒÉþ!\ˆÇ“Szɲ|ŠOc¾ŠÍ÷9x‘R 4䆷šÝøœŠ.CÂò¿—¿¨6Q‘ºJËÇ­ó εw΃Wc®¹%SÀ/Á¸–úx`˜_òd@ÌR˜eÐêŠÍË “"K€%'‘:. æ†j£|ÃDÑ‘á)¸«~ú©M¶lëýyÊQ{ÀNYµÜZ;[ÙsÇéC.¾f{úqÌ‘o¥õ4·!¡F•8ÞÝiwßy­_v:á öÜö}v×¶{ìc7þÌŽpÉÜcÛ¶n³b– 2'VÈå§6=Òà`Ç9RÏôè)?rýmÞ¼ÙÝI@¨,ËC V²ÀÄž_ò2Ê?5Dƒâ§>MM-%<–I•¥<ÓKšsò)¥ ÷“0/ú†°wgÍ_^%BO}÷Þ(æ!(¶xÓôöX]Ï&ëíÀ¢nûkêØÊx`%Șeãx#=3ÏŠÙ.dB2–p £è÷SW‰œtè»"¡ÆQÍ£VOÖ3€Ìñ¶*Ê—\ŽÈ.ù]ØÆT‹/–]‚<–<6ÆøÇôgL½Ç%j6æB<å‡P8à‡o8ñÒg¤®ÌކPI ØÎ[Wó‚OÁL4ó¹|ÒKõX­-Ò¯ Á¿¢€™GQ™ëA[ácÚ"ç>Z")½sÊJŸ(…+£Ç~úøè4_€$™Lä/åg¶€·™yï†6`H ›†ËO3M êÏ ³Å=Ä:®ۉͬ¯=hÕ ´×¼[m4ñÐX†ží[öp Ó 5°ïÂcGç u×ÖÛ¼Òlkië`?¹ßÀzRN_߀íÝx¯zú%öÑOÚÖ¯®²–ÆzF`?¨Rk[‹Užuº]òê‹ìÞ ØÊ+Ùäü’Bl‘v`mm­ýìg?s¡Î Rã‘H8˜Œ1uÚOH˜“ÏáÎ箾›x]õ¨û¨>þ|RQìQ‚Ï• ®†Ë”@|Ÿ™k™hj]ŠŠrò—–^©©;m{óÖP»ÇÛX×P òšAä'øßc=Ÿ™=ukgâ3nRëÐë‚Úµâh×DRŽ(Mò„Þ'_õ^!´ ÊÁ­ÚàÊØFûìRºIñlñ%ø#Ñ–{ô§6 „Ù£WW†#èÒYŠ0 eëWÜ)"f>(ÄNX†—èg üj"Ó|ça ñhŒãÏÒ ß6ö›Û@¢¤‰³ƒåh7ªcfÛª|9ŒïEÐ_:dTÀ®í?GZsï½DŸøƒcjÂ`4Ž [eQ‘ÿ½€×ž£‹úh\;ÛåØ[ˆ¦i3¥Réx#„Âv{ܺú;m‚¹x׈m9°³àu¶ ²ÂðÛ7%¡NZÝr`?ûÑ%VÜÇ: ]]Qeó‹*™˜éÍÅ¿À˜Ú{G†Á’ö Zݳ;­nU¡|Òé´2m°OÕBÇ¿ývt·zq6f*/+·+¯¹ÊnÁˆ;¯—šìÂÜrJúÇ?þѵµS°dÉ— ì߿߹¥Óß–1½|Gj¯È7Ü0¥4„ÓCbˆi o¢}O>æá^ù¢×ª‹¾s<Á?™ø°œ™¤Óó™xîHÏÌg‹3ï¨à0Ô­³}‡õ hÕÙ»ýˆ60ÒíJCrÒ‘ÿ†lX|q6q&ê8œ—‚„Vb諒æDÜd­¢)&º®Ê|?™+¹¥ Ai”N0 €^@”zôP´¬dlÏEÖ+ ?‰´aùçEH1G߆CÀU­Sé^3ekOˆUºÿSqq¬Zqcö÷Ÿ3»Vÿ´¹ÄøB5Ø“YøäÏÞˆ[UÄ Ÿú¼ÙeW™=ZŒ5Ït«—!ªúxº”÷à8s ŸAc5 !/%eà!VÈhM‰X†¾â¢RTõ1T4ÇgçD`/5ñ£6Ív`Õ„Õ‡0GVɃ¡ÀÂ_Wâ´XT9êÜÖ:€ J+ØôvX?½˜½` {ÕØ‘ÃÊ×·°o+±…¥VÛ…£Çœ,;€ä??È ÚÙEØÅÒáàžýèàžÌ…ª7rLx>ßòÖ·Øú5ëí¦Ï}ÖrÊ ­4¯lÖ%¤Ñé7^|íݽ,ÿ̈€M'n@lÿŽ;| â«®ºÊ¶oßn¡—Ѓû°#gÓÕ ;­Ûì‡ß6[¶ÎpT‹©ÊRõB/lYŠH ˆÞ‹¼c³ à _µÜsÿÔÓ—`ßtäè©B” 7h-Ó`øR;êÔDƒ_ÚE(u¶B4zAÝÉÆ9(Ôµ—rÒãê]lÐåŠÉQ]è2ö¬­GÆ$’¦¦#ô€"Åmû¶=¶îäµV^Ynù1XÐl'ˆ™ [sãéö¡XpÜCoô¼sÎ¥¼Të'Å‚¹öž€²ÙvÔY}éÒ•Ëm×›}÷á‚‹/ò6WVÏ·K/z½å±]¥à˜Øï¦þè;ú“¯À»ï¾Û~÷»ßÙüùóí×_ïÂB”›ÜË CGÔ†¡šƒ­ç~€üU˜òQÏM 颸%'&d˜öëå%½9`jrùê¨YoÞóK{vÏ·mû¾[lï¡Ç°Èã Kd3yÙè°®×Dˆƒœ‡ù“B ­ñ­Yù¼WœŽß&ýˆOTñ¨òÓßé9Ê]£ôzÐÓ€$ùPYø´4ä_’¨­çhÀX¬ág–Z÷ Kiÿp‚Í‚`‚¸0l®ÍqB†Ò>`ŽáÈT>Þñ^³+ âÚèÍaóe¯]â¦R+V›}ûËf?ÿ dåZɳr©3“Ùš6»ç³ýˆ¡¾œê ? Á¨ÿuÍ‚¨!Na 8QXîÍDibŠ>/çD¡T°Mp ËÜZ'f fk«Î+Ç;P—-­šgC çBHlN¾ŽæbH±&[³hž=ùè“H¶14ii´tî<éÔ3¬Ý€]Ï?a[¶m·óAoßõ¶uïv{îÁÞMëm×°U—ñNa:°k®¨ < GŒeh õ.lR›¨žÜé-@PÂE/óWuí ¾„Z6ú 3@3Àb¬#Gé¹QœSŒÀz¦áµ¥«W2´¢œ2nu­-ÖÚŒI(˜±¨°Ì¶íÝccíVÛÆ¶ßÒeìUç Øã´Õ^|¹TúÑCßÝ€ ‰|:ëý¥œã$Ö3Î>K¿ÖØÖcg¼ê ;Ì =ø4dëñôû–ëßém^¾|%çæeÙe]jUóªÊ§ -?Qôp´z¨o\’àìúÊBØÿ7ƒ8a2Ä/ø¼ä ±¶O|T5a¬èÓª%z ñY\GAH[K:¢mPË̶l |Œº– §¿&fÿ¬j› ¯€m8ÕË?6ÆØj™¥~ÁñZÃ%¸ðÏ»Km"ùëÓ<òΗÄ&^zŠ—ýóJ õ €„ë"zG‚&I’ý” ¹xJýÊ``)ÁìTúÿ}-q[¯UžWhý4‘¹´Œ_pë§a(Ú®½;,)ûIkO°ô"4Û0oí"æ°nÒ¦B/ÃÝ[žG#0϶mßb»·í@3-ÓÎX:;EãvÆégÙÊÕe¶ïð~ ‡SëpþÎ÷¼ÛÚ³‡¬«Â+¯ºÒ{6 •׺þXH âä—@ŽFZ1gNǤU\MöàT51d’‹‚ Œ°iÜïì:í0KnÀFdZ6ˆ}o'^.vò§ÍäÃ^ÈBe¨ÂúŽOHÿž~¦´WBÀU(Q+¿$~QÕi©¢X¥LNÈ41'“ß«rTT0ÏQzA ”•¹”WôŸàµÿ‡¾‹¥üRò(?õüê4½ÀPAõ9V<¡ÿ¨éª…Öú8&²·¿Óf¨;̤6´È9Zòîÿ\ ®5{àvÒ,†AÊßüª:XêÑ ”ǽ¼ÃãM.Ðli‘ž!bÄ—–¥ÙÉgÁI¬à"bâÍ”I5;q7µ áijÜDÒ¤›Ä÷’^ï7±=d•ã”þËúErÿk‹þ¦¥t½ ae a@F4è_ ‰ª˜&Ÿê­d0˜Îús/¯¿ßߊ%êɬÝKaõ`åñãÂ!ñˆ“ì5—†¥Yä\ޤ¡!öþ™U#$<ãB¸2š•Kv¥-ê/|Å9#ØB˜ýݧYñ“^‰³rz ¼†£C6U²xmÑŽ ]pePƒ3&ÛHµ÷±xÿh=xè^U~Ùa6@¨L qHÖºÀZ¾)€hÓµ@—ÚÑy€Ø ãJ©g^¦]ÇaŸ»öîfÛ$Ýš0ÿì¶M[9Íqv¹ ´Ô5·–/¨´"Ìt³1S«©ÝeMõõ¼9T…ýîLKض¢K7Y‹LaK>—Å·ýTð¶A7;¾ð¼WsF`£dÚ>\e¢¥xåÕWÙ™g +ŽROMÍAÎ,qÊ®åÀ ‡ÄˆùØD£Çøhˆ¢G=LÇð練FV/ÿ÷¤2“^O-!é)J}+º&’L{$6ÊT†ß*>J-H²cfkæKåÖ)=Ï¢ò²²“¼žk,Öüo–Zû;€þk=$t¤‚<É@Oߺ-}4õ¨ú^ôMn A°î§ÿ*—(6ÖYv ät¯<ܺr\º-<ÁìËÿÂ1tϼD¼ï=á$„ z…Ö-¿N5‡ŒâÔ>úã¨yÊ¢Ù' r Y/xÁö°,Ë¡ 1ŠªéWjö§Ø¯~Fù›±©˜£%!¾Hn|Xjý/à—\L¤C E톊ò gQO-)¥ÈbЃpܬ…¨×g­@ Råé ¹ §˜ À@‘ÀÙŸæ± „†Ì4:eë¶}¶oûn›{ÚrK_¾?–X®­^¶Ôz! ùx²dÜ…;®çk8Ö‰í5©§jÕT@™Ï#|ËÀ«W-¦%kM×]¶Í+FïZsg K#ßý¿û%ö‹09î±gÊR±\Ë®(Â%Y§ýüg¿°ûï¿ÏÎe»ð£ïüÛ±eæÆë½_8íè}¤‘'h[-qËSâN½‘è¥ñ%R|Rˆ¢CTÈ4-IRjn§dˆRN‰œH£~w%›’=)Ò M.#ºWf¿ÓXAäÎ)=9­…A…@qÀhÏ›ú¯ô¿åú%ž_4jU ÄLPzò'½Ï÷è3G|ŸlzÇŸ®nß5‘–øDPí¨ý€‡’hQœË8Šr¡Öúrhráf‡÷çš}ãfŸý*ˆ€üóæ‡Â$|ýäA<–ÏCñgÙß~е?«é<û´§ÄÙiL/ÿůʬ!0¤´o~ÎyM?r‚<.ì†pÄ &J‡ aÿ_ˆSjÀêO׬”:°úUÒÆ‰@cì>óOgm ‰¶Ý0ë&ª'HÙoÐȱíÇ­ÔèYÂ{µiø}ä¤ÞU«Wù¶ÔÍ?ù)õ¶hÙlüë¬í½]5 ÖÇú>kA¹•â9¨§Å2 9q¦oˆ½uö¯quUYPlÏlÚh ÊKmÞ¢•÷<`m}l×àˆèFG|AY!¶%XöYS{]iW^{-l)è„ÑÎ2ã5gžg‹—,±[~}‹-Z»ÊÞô†ëlÉÒÅ^ßcqÑ| :dÑlTˆ^è^qQ|ô¬k"ÉL¾ž¼K."J;qLFT”rJäÄ7< )¿§".¤ŽòF%GÏš3]´€‰kÕç@Ïd+Çõõ[ö¯'€þ ýÄ#=s.!9?|ñÄž¬§v-$*™)(Z\켌mXV'õSh“j‡V¸¡va¯:Óìĵa‹îu¯3ÔÁ适 ä¡)*wér€ø,Ê£ÚCs“Ùâû—KÞE!~Ó³wÜh¶|…Ù¿~5`‡¦Ð‡ÞfvýŸ±…Ølvñ¥aé %L-pì1F0™ÖÁ'iœ’¥µ¿`=Ç*Ù‘ÆÞ]‚y?‡o%~y¡úÉÕ¾‚j>»a¶@4Œ^Q(¿W\«—tºÄS f0U±M»öÔ²«°Í¸äzøÙVR>×¶íjDJ¿¯<ÖÝew=u/BÀ½L’q+®*¶& ²ç`΂5a!§ÄÊäµ®f?[“i ÿý/ê‘cäuàLd™Lš@ *kz˜)n h$2èûQÚ¨NDytŸxU8 t…ÿeýîÊ5¢Þڣǹ…U oq½Åš¾i±ÚßüŸè™ùx ’=…k«ÎF4Í’>8°øûÛP òS÷Z­§U¢%PìñœH«÷BQ¾fßÿH`D~l?þ£Ù¥WV];b²9%ÌÂö)6¶s @ /¯¼Î!q•ÎÝ‚4ìL'×üoû v2ûÝÍÞL“ÿñÿ²;Ðböó?™ýõ_á=náßÿiÔ¾øI³|h.é›!s¶$mÀ¦Äš¯Àô¨[>!¥únÍèÉPAX°æ@=àeå؉'®³»öÙýôÛѼö:€ñ ‡‰àÓ/5žaõ éQFñ¨Õ52ᤃMëv£p¥vÆÉnð3(ÍAœ…TÀ¤#nÏIÍuŸô Ë«,3$¦FUI²ƒ [Åõ…á,Ý5d·Üz‹wÁyö*BãF[iiêšÉmã!Ù‰Ù ‘fut]“á`¢˜ÄËHNàñš* 3fð7/ê'ñ •æ¤%Q:«LÆD ¦7ŒGk‡}vÄÝRÁEPk£ XÃßÐÿ?§ô)ÍŸÅÌö5ØèäÒ>ÞÙ{òKÏ^ _â&B¸B1àw?šäÝevÓ¿!uWŸÖ;¥QnM›z®×¼Ãì­ï ï´s¬—GäÁ¨¨C‹Í~ôSÏŠ,Šüº¥YpåöÍÀúïAðÇýüEfè‡á„6½”ô¼Ï¬vÿœÓÌÎ:„r¡G&'è +p?°™<4uõM¥ëð‘]ÂqœNý°ùï"Kffƒmyq®¤„€#h½Š+àè\DBj$r1(¡„‚Þb¢<„«w'Sµtvƒ÷Íìé¥yÅ1zhÖDSó<‚ú»,€†¢¨£ t ÏwÞí™´L˜¿`¾¯ÅpïúólóÎC~¶_/lÿÂCy©ƒvP/h;3/“f8ÃÅŠ €ó‹æ¢m׉RΰnévÖég[ &Àì2¬_¶’9Ái1NCRY’ä QCL¾ ‡Q­«^¼ØÝô2ˆ•¶×^Ûùð³ö™Ê>÷Oÿh§r„y\ Ä£„°®›á¥7žx]£ûiÉ¢è)ðOâ?øÏÅ™VjÒcâÛa<ϬuÀÕ7˜ÉòäƒùÐü£Õý{-ÖøŸ–zé}˧ð¢si™õøc½ê3sT±zsÉR³5›ØÆ@óe—Z¸ÚÞÿ³3/ÀàæAÖá0}ÃàZåç‚L ¶›ëÂ%”ÇÍ9 €åkÌîüÛw å/*E…·„Œ ÚûA Ï>n¶m#;0.ÒMÊy¼ç*âŸüÚì/>‚p$ígcÃÉæÓü2b¤‰.Ý Wâ⹬²d9ïJ´«Opç¢Úß—‚ý¥/`—6 ûFò¥3&v÷Ô1 áJ"Ægv`éã»/Ï ,ä; J(s§Œ¢b ‰hèjä̹°lÌÚ†»¬vï«XRdká$pR ‚®Ã]5ê]i¨|õ´4[úÊR¶nŠÐ CuRl3DuûOì÷ß|·ä'Â'dÿùÕoYNu©}çÛß´G7>bŸüÔ?°öãû>¹iªæ8v=”å%þT9€Šò€òrz˜5åazJ¯_ –ês¾#B"`w[zêc'ÌjSFØ*Õ!ç€ N&È! :÷äu*½ˆ¾¯Òõw|AuÐëÔõù*p…AÕá*ÀQTiü» ¼ÃáζíØï °â—ìµvR„ôo¿ªNÊ)3†š‰á¥ÌÃ?Áª?ùDÒ2ÊýÄGÌj:Ì®ze>«_„Là ³”ÿ0Üžñ=€úT¨ºBPÏÌö`BÐ1žÀ*OK¡™=UgÔ$”)0rƒÚûPŠÒ8µ_fÕâÔà0Î*)ô'©¼DÐÔÂCx‘x˜‹êù µHƒ0Rcøÿ¨Ô'œ@IDAT]kÕCâå”ËŸ!šš“ %%x†Êê”Þ̬¤a§“|4¢µÍ¨õâ ŠÍYÉà°a‡uìêBYcØ"ììãd_l ¼"¡œV»xI/ÜÄb«q¡yÉ,¨jˆ‰ã“k ÞýÛ÷30ŒŠ{º„~ÎPï"rÓH?`–†Šh8rv!"°€¤T/À¤zZÜÕ·Â'u;kAýÿJõ‡k0T @}:ÀÙ½€bk2J' Æz;hEéìŽ9O8á¶ ‡¬ ÁN¼ÝvÛoìÚk®´Ê…«ìÙCíVáÎþ ¬é†mýªU³´ ._j§¬8ÍöÕÕƒ¡Sm~I•Äå÷œX.ZPʼn6' cÀŒ`ÚÚºÛxœxð8ÈžMáœ|+ìÜÚhD`(ã ,éš[tD 郶÷þ_ÛÇÙzß_¼ÇæÂ©HyH<õ²t6¡ý4‘®@r=ß141" xÌÄ3%ð™5Ó‹D´5Z×ËÂT%…%Ð:ö±öš mbµ¿àþC о »øè̯¨*¸Û·éÄ>ŸTmö†ë0T¡ñUâ’xµFb²[—¦ÿû`ÝD±Ý ’à3£|Ÿ^ ¢å€¶î1û/¶ånü`  Vâ ;ZHmWìý¢r(ù}p0Ûü8Ÿ{ÜDú÷—‚1p4¥iblF¹ÕéÌs²uú²Ã'9÷ ªƒ†ƒTæ êîŠKo +,$ŽK(ƒq20ž5æ*˜÷Nýõ¬‚¢‹ Å·F8„wÉŸŒR½¬+UžÕàÕ‡•õŠõu äÐçzè´$/3ê%?H£ÃeªcÉPaT»vá^õ+IÞ/©fíû?ø¾ãEøág·ØxqÏÒ!½‰K0T,ûqGÝ…eß¾Ø »˜!` ÛϪ½úNÛôôSÈâV¶t¾µÃÚÊÊ|«¯«å˜*NÎa㸀MÞôŠä[Uq‰UsÜ@y!dˆ¾ÀR1Û:6ìµå‹WØ~ø}» ÷cé̼,¤W7¾ûzQîBsŽï—^û‚®š þ"ܾào”÷ÈD¡XM¯8VË,mômo‡÷mý*Tÿýø5dbLXØñèz÷8*;*_õŠêÞõ÷(É-FÓqïz¿Ù½¿G’^8µ€_­te;žEç“f¨nØ÷bvûo@pà7¿¢=x´½íûß°E§ C©SÖ™½û}á2䱇3^8ñ™½€ÿ¨ý÷XëÃ0zx†ò•†©uñ'ýþ¶fÐÖ°¬P"q¼šÀׂYq2ï¨^}õêÁ—ÏciµüŠÕg‰‹¢šØ~¾ (©¿$2‡ rLÌJ}Ͳ¹3aj¥0‘ ý2~gDUQ%ÀèæöERpv‡,¨½F´sÚ! h§@¾øå€SHã0Ô»©IgôXn›íÀ¡ýVÇ9v‹««íA|ŒJÀ‚À°ö¾fï£v°k¢˜ÕcÏC×A¬>= 9Xg‡4bG°'™iöȽ÷±Eˆ‹¨ ŽÃì@0È Cu–\hKÐÁ–!B¯_;š‡ûÙh9\gû¶n·Ïýó¿ØÜŠlûÂþÑN:ñdÔ˜Û±bìƒ:h]wücÖ~ú„O®Ê›ˆ¢ôú¨a¦D*ƒá•´9e®eŽþ‹eÆ¿„ªêߨX/G„ƒ8"9˜që@Æ!ú~øœÊž©üðv¦_}YT^×£93 HýQT5ZA)¯¶þ ÈöÚk‚Š­AEAä#×%1¼Ày N:˺OyjÀ B_Z§_p¼Îàé‡á úOÃMl†›(†•“Nç*l(?;Ì^üåüê'¬ùI--ÄJg`:ðhJ3eø‰È1gt›–L¬óEö" —w©úšý—Ä·õ.ÑÏQ Z€5Ì¥nÔe³¢ŸÍBUo‘’NŇã#C[ÓÒÒ×ü£l{ÈÉzbb踰!ËÌ*Cúz‡‚ ƒBûDEuTw>ÛtrÏÝ‚xuïÞ½vñÅÛw¿û]Û¼e³Ýø®íË_ÿª7ÿ$[±jµÝ³õ—–ž›mÏ>ÿ €Ø‡:p#Ý;ݘ…7¡x *,ÂØö>Ûýì;oÍüDâºÚ:X}Åà‹pš™ÊY‹Q6zzÿn8‚pØyô»ñíØ]`eåÕö›?ü¹¤´»4Ø’xHî‘ÏèáHÔõLHÅ&ºóòí•,Íb¸IÂ“ïØ‰ôiŸÅ[PM±áv ½D4¢zê{ú{éA¹94È›#3å™JÓšY0,Yj/ 3\Ä)­f8]«ªˆK±ÿP+†¢CŒ­B*ZúêK¡ÖfKêOÜ„‘L›¶äÚÁÝÛÌ>þq3p¿›ï®Ê kú!`ŽÝU„¹øAŸRÝT}Œ;í»ßc›ð[± Aå2d w˜-hùâ‚À¨ÛT!ÏWšsA ŠŒ‚vªüo c«9/¡_Ÿ"‰¹ï 5HÂZ`¢•gŒ(^]2«a:;®Â¥'¥®3þñŽý¯Ô 9ÿÛÙ ÌÕÁr *ßçþeå/€uË´‚òµ6oÍ›`ç=‚| 2³•Ÿþ~¶ åŽK¶÷âúñ€QSsÀ.8÷<ëϵÍûv@ òY«Çl2…ÕËéZ¶ $¶ƒ 7cïßÕAy ëÀ­xýíVÄcŒ#°q׎oû|Œ‘ÊÑ/haÏÔ:‡{ðžƒ×´Ù† ¢Ù¸ÒîCË0§ÛqM&‹×t¼èf ¦á*¿ú%EäNpǯÀ($f‘~¦A€Þ)i"ù‘I•² e{*Óé9œä›WYh™ì¡¥‚ÌÜÿ¼ ˜H>5ï‘e¿pŒ¾%>)Š ´·}uÚxqz5CC_ÓhÃ0ÙægÃ’ ™ª ”¦€zn²Ö£xĽþM”ÿ÷fÕ Í>üN¶þ. ¼ÞìÿþX2– 7šýòû Šb4O!€çCŒÙñuŸ ìþ¢F™”'}€ûþ„?K‡J°Š¾)!¤‚ž4%¡AÞFÔOŽ ‰Bb ñƒ©hŸ+ùPËf·c7€Zȇ¼ëõÞ3òãaTý…rY½¿fÿ<k\¦ÂR+—„DÝtñ/¼ó®À*p»ÒPÁ´N'S3ŠÐˆÊƒêr4kçšP£,qr‹W€®ów' –z!^~ aÍ¥#pÛm·Á^bï¿ÍòñÝ´cû»ïÁû-oq)Z€Ãi]‚fáÛ¶m›Í©^j'½êl Z¬‡%B~í%lÔ^2…Â||îo´šÃáv¬ Ädrí•4jiU¹ÍÃóðA´DPžƒ7ˆ83# Ï©äTÝtME }z­ Îáâ¢uìQ?Šérp²éKœãd/‰Ÿ Žž&á<ÑßþÂgÃD’i7þI¼Sœ´Ï²p³&3èªwÒÇSæ¥MœifŠ.A›zæí×|Po` ác˜ÛQM ­³5ùð8nÍ5䨢æsî¨Se^A@ À+ç^ˆƒe½õë†ð7Ÿ1»ãI¼÷~ÁlÏnÁÛ1ìy³¬ñ@B$|~B<°>b­N ,$$#Ê­o3•|© ½€Ç Ü€v‹0ìß³õÞÓ}ËO‘êÑ£õšSt%Jaõ+ÞD¬ŒÖ;ÉÈÝ9’¨ÁüWYâÖ¼`ååF¥7ñ‘aëx\lFXKÌ ½{)AýpÜAûï y¬­e/¯ëÑþrssX‚çÒÑc[P²Ñ!¿ééHÓ3³sSä‚Û‘P® ÆZ{Š›5§ÌæŸð&ËÀHA$Ým *DWiPÉ?¿ÿøã]w€$æÛ*L†Û9M(þð¼ó.´Ûn½Ýv±‘›Á 7RÊš“ag,_íÆAímÝVÓUo§¼ö:››YfO³ÕX4?ÏJØ.lEÛ°`¤30×UŽ ÑL¬.™Ç¡iÆiw6 ñÏÅS±B‹;;°Zdc`ýÐ++/Z]f›Dü‘M­™2¥¬éP‰bµ·^Œ¬2AlÃý’Ãàÿ.Ç“¼Ä‰Ã&‚Cåvöm €Ë/ãêK3o.Àãäß’ ¯ôõ¯}›Ù3Põ÷Üë¿ ‡² `âåH<¡¥xÝÝoª ƒ HôÏ_nöšóçÿK³µ¯;hö‡ß€ žÆ¤¬QÉkT@ìgw@ÙùÛ½“üp°în5™Ïùò£‚ë÷¾‰àðËA›O ÕtÎɃ:iÍÏáG¾# Ü#®êÈ@¡ Nå¹f‚y低1H)è$§t@%|Y@´÷IÁ d )‡ŽÌËËOÉãø¼™à-‚Eå`4©œ¼U?5Dl…> ð‘|Ä>ð€ÔÄÒ'<-7ñãÚÃçè­Ñ r|ò“Ÿ¬ùÂW¿w`Ū5Ë»ZÍ/==’\À¼Ñ‡r0…6”ÓD¹°ž»â k«}ØzZ÷rT7ˆ"š­©©ñïŠu•›n<ÚÇòŽ;ï´+.}µU-;Ñã‚ì ócöàSOÚ©kÖÙ3kiù¨ƒ%€!°æNc»°rÉkè¯Tka¾S>³j`¼¡‰hFNpç]wÛ%žlgŸ}ʹm·Rü€\JáEÏB±{4†CL(_h¾8;÷ØíUË®1ãËŸDÆ þy rlÑÒu¨oBM¹›CDF¬¯¹Žå ÔŸö75·[.µ—Ì]È‘e¥V^QˆSŒI”Ù¨,MÄö1¢ê7MPÁ˜°*ŸdÝjB$¿ò¸‰MƒÂÑ3$%&Qb­.JfÂõ³PW§çà~Š#ãs2)ãË»M´M[r8mö“uô=ñouÂÁ ]{zøÌ)¿\nã¡Í꘭?%j#@§º½å:(:äs_ˆaÓ¿ù9¶7ʧ?>÷ÖÑÊ+‘(hí=ÀU=/ ®+8/Sˆ¹ÒõzŽ‚ I7eœx©æ¨ÿt½ù»È"D&ÎTq/„hyÐ ”ÿË`$w`>߉ñU°²O>ĺ²“è „oÍÜÀqë8{ßã Ì+íq!€äB\'ÇL½×{}<‘‚Í÷BíÿŽ7ò<:pЇÅiGŒã A:©'&W­0_vfše—0Ò8ûX8ŠÏº_ØO€‚°aZ0 ’ðïþ‡ž±gžßfëOûõ/n³·ž|‘-ª^ÂÙL4œ+OcÇa좴؞zòIûñ>`9e}6œ‡kp”ê÷ÕYk[¶­¸x­å ôÙž¡û¬¡N£Ä†çôY^V §uÂÖr8&é娤5á1܉ÃHGzäZ^š"‰Éã}¢Êú ãêF;ü&Z2åá(q3¥I$¸hÖ|’r/qì@7’ð€4Œ^“Ø•=öDž(ôøoÔLy,V5´µ'þQì?;¬Æ‰o¶v=kî‡Ù¢{-Òt(9Ës‡x­÷/°?úÏ°ç ¨s_cöð]f7^!ejH¥l!ýÐV8†g…ÇDÄYù'eiJ1Zã{Ýû8ž¼#’®!ÅÄo觉ljÅK5YÈúbÙDðù#dt#hÔ¤µþwÀåع]6,S¤ì2:GäÎòåâD)Dø 4·ÄÖ‡ùÂSb\£ëDޤ›ãü(Ëñ¡“(5W}àX$S âH1Sm¨é)0×Ãôˆt‡Y× Ñm¡uï…X1òHÚÇp.Ê¥aD¾¹èì·ôÚž†PM) ‰+Èæh‘IVÜ‚ÄéÇcm´û žsæÙvê%g[fE‰õ¢[Ðʸttcf|Ò*ËE¨˜,©^d¹ÕÅ, Ð+‰ô°}XZfM ví“•óí´“8X’­|Ncߨ7¾E,4»©·NjE},…mœ-¶š–:Ä&ÿ0u ‹߃P‰!Ž:)¼œá×+Õ ïõ9ÆIo³R @“iÕk$`iìÆŽÄÓ8gøêKŽRÓ4R2ŽéíâÛ<茽Fâ´ø¾½àxª½÷bWÿ¤ð¼X³Šc¸ÞÁšþ;ìý¿žww˜ýð[ä'-8ÂVà ”Ð.©~Ë­„X{µOÚƒÒ¾{þIhÏâ2ÄÈ«¯˜ƒÛ~Ìúþkäå|îuHÀ1ÇêJ}§BUÝO ĸ%e"VÞ‘2z-%o/ÔW6™È>ôâùÂn˜Jcâ‰ERGI05à pt% Î×ེPˆƒðBðç ^Ä—ü"ÒwR*éµEò®ñ ó;~é®ÁÇåN€5ꯛÂÿüÌNú€ÑÖÂÄ 48ýlÇkVÁr\‚_DËþÐp¬c¥'JFìe{0vd³x2‹»î¾Ëí?`k.µR¶¼û¿²ÊyEØfkOY…°¯!RŽÕr†ÁÞÚÃVˆåH%Îàó ³oL>†ú­~5:0ØA@&ú§ûðç•~ÁŸÄdH´uJÒ#⎈ 9qÑ,gbÉUìcF®óäôßΜ¢À02BQš/7¨Z¡9ð1¤x\G`Å:¿Êì+¹tDc¨U ¸Ý~ó3ìõ¯Â~þ'A;ï7? HÁël ²ò{ò¡@%Å×ÖÙV$$)À Ä>â ûùç*Dq÷“Ÿ!„·+y©M•Î@Ô¥E%¼Þ{¼à‹ê€956^þ{_ôyK-ªeû•mn¶]Ó2ÊÁ¸Hƒã•*̼¿\€Asžr&à_iO@ËGNéH€)ôn2¥^nx©ýq<ß MBçD‰ûº~KÕAíaéIo¸9ôp?výqŒtx!NÀM$Ajg Æ`ãz,¿d1B¾ agÐË¿Z†,2âd;ðï|Û—»ví²R,Lzlƒ A)Þø¦klNá+¨®´Âê|ôÈ×X#3ª¤bºå*'Ë !)yYÁ—`d&7ÎÒ\næ4bm­?ý”OÊ\ú¯]Œ¼ÌT+Í.Æw|§eYÞÜ[Åqe9èŠêÈlXŸ0>d^ËÉŸ™Y¸‰@Âä\É÷¼JNæEቂtA dzw$IÔÈ9Åöùb¦½õí$ïµMÆtÓðÒù4‘  Aàž”I¡Ÿû ‡g²–_¼„qx}PÍ?ÏìK?d-HàGÄ¿ )þ® z毄j“nÓ£² Þ4Úâ–E¥÷¯ ØAqUðÉ:(^¯Õ¦|2©éÞ>½x‰! ‹ïƒ@¡œñh­`³WýÂÆçrˆIñ#ÌÑ?ã"•%bLr.€>•ƒVӲ惈‘¥RÙBxµDcBÁª!­‘í¢Oá[(Ÿj]Ì• „’™ö^dû’–Q€ö)f©ø³pA`¨aM †q|¬Ž_3Ž_¹Ì~x¥Ãä#…>ÃëþÞ›`ëkÀlY ¹I¡™Ú0` Å wÓ!˜øL)QѱQÿ¤Ñ®f$Ü·ú\8Tq$`Rˆ(ª¸ åY¨u¼ÈEM¬ uÑ^{èÞû­»ìn¨úé'Ÿ ×–fU‹VÛn4HZZÚñ%ˆŽK=^‡5…)£¶Q,[qÔZS¬¾ö ÕoÛe¹8³DÝlÊC+±Cí„›x²&€¯ÿ½j/ê'iNÏœO Ž™(d’‰B¦bÐ$Ñ ÞÎ,·!ÜÙx¯¡{XÙ=÷Ba:¥×ä‹òÍ\Fé4µ¹8ðËQ'~Z|ï^R÷ùP{í¹³!ãáŒsYlbïŸuúWoÂúîayÝfKIS C§²ä[_0^8äÑØêO€O‘/äšC3q ¨fz{Œ8_ÛS°wü¥ÄøPÞn‹Íÿ“e-»ÇRò·ÀÚ÷ 5(ËÖÞ3ñ­ !¶–]Ì^¿jy+â¤cÁÒt(š½ã#íp¶u ç&ÊV$µR:ÂZ&ŒÅw„†!%!)Ñ1ê~œ¯_i@5¼û±ÌHïí¨¯ííj¾žsÐñ Ýл ”’INÐ`QW¼ëˆ²€úGêÃJž‰XþÜÓð%oNHûh«ðÌ3Ϥ³Ó¬áP«@›äÓ7}§•®EØØÔ†WØ-Ç®ÁÉè ´8{Ÿw!vQ 3Ãã½v¸± |â>\›Â‡vn·nÖü•+–Y [\¶×R¹ÎáÀÒR¬‹€¦àìDc5Ɉ«žG j@r#¦$Ló¤ÛÉ$3dL¤ @Á{Öý™¬ÿå¾Z¤R:í»ög—ç]d¶îUáó˜0zŽâä˜S€ŸÎÐåƒ8¤N«u½ p$«ºòr³ )ó ÿ…+­óB-?õ·fŸÿbØû_†Yœ¤q.,}X{ èÖ­3“ÁŽx•óØwGØ s•Å÷_ˆŠÏDý'úGÝ£¿ã$vÀ'ƒ€ž†§dvX¬b“Íý5û–÷£ÆÒ(²åš¬rø1Ζ_J:,tJ'M«tf ÛÎŽÖèÎ÷aŠs¦åp nÊšÙC§D&òªœ×d%ýŽ8æ.÷4=!E>îFwBñ3¯h Õ5ôi™-uÛ)¿æý™ÙùßÇ×Ùr4ÕéMõ=Ø×Hç â° ÌÖ ²B‰eIè+²fV{Ë]e…=ÖÙ´‹Îf=¦í-Šä·Üz+ši™¶áéG¬ri~þ2í—¿þ_`'Ÿz¦­b¶qô ¶·Ù†Gµ“WÚ^†G9·½µÏÁVhe6„û«tŒ’ÆPÎ+Mç9Ó6oÞÁš©-æ ‘]Û­£ {&mŒ S… 0~0’hpÕôÉÁåáÈ $õT”rÆ\ÑË<ñ;cÊ0§H¾¤ @+y…`†œ@· c«PÄ·Œ–ÛAÖÛêÄŠËèf¨È' ×W‡=”—…—v«§Œ<ˆ×Y,'®¹Áì¢ËÈ žÕþË«)¯‘í³?be·]Ua»nérú‰ohê—s•œ&Ài,eI‚lÌ>r#:ý?2{ÐÆ·å‚K'Ïd|CÖ£R½;Þ s]Ÿ‚¢¼NíüZKÉ­aMÓè2¡T¶´c1{¨}|L„ ™•Ûé`qɼÔÙrR#€©Œ³ˆ Ÿk@ß ÀÓù¢öš’WQÿ‰Kšv ’;RÂ@LI4‘zn^q`ÏFME-‚,³§½á™U¹¥¬ù¿†`9À´rz€ù§³äéÙeûÕ.ð±œQESÛµé*ȃ'w›u;Y|Œµ# €¹†—û“ÿdw†ï|ëç ºüp¨Íz RZÛðRæ jŽJwÞ~K/©µ´\dAx‘ÎÄvEž‘ä¶#3gˆeaHŒ]§÷¥y¨£áXªBñS™áÉHñÌ=ß(d¾”¸†7¿¤†³¶wÀÆjÊ«^‘ ñ}ECb ƒoä¥B˜Õthë—º? /›Æ25@‹Îñ3 Ð Æ;Ú!‰^½‹ àbŽ©éB‘W ²’ª¬|'úøº+4GK-j9ᧃ™ É}Nè´ö—àìó.Dݴɶï=ûp°KpÎåbEˆc\’÷ù …Ñô~d }°yéî…xD“ƒ­w_=KŒªR[s jÈc= ¹S}?<5­ŽBšð+(×áɹ‡¤¼3•Ýjž©ý)ÛtáE¹5Ð>y?¸å öÎÖŸG›w†c±ú$°jÈ9‹Ïµ‰Ýtw>ˆâío@Çþf´òn˜«Í¾ó¤øW˜ý×hfÓ™çC¹ïØAڳϡL±üRE(e3þ’Kyç Ê£ª ±h»wV~­!²‚ïH~¶ooÈëCͫ٠šc(訓Ô`jËÁodÕã–½ò>K«ØÎ–)æS™x’FÖ‡!ôMˈ#«êÈ¥ÍÇû\̈¥b;ضhÝ?êkûáþ} ¾Z€Ÿ½LˆÔø¶·\ÑúþÄcbô$”ÐP=^„sˆ8‰4‰§Y¸¼âÀ´:ƒRHA'¸?³£éÀMcñ‘9s Ê>‹?ˆÒ ´(åD$óÐ\(#U.‡-ûE‡¹À*˸Â/¦ò¬åAQÅbØ´,kªÙè; Þ‰&- Z[[ýO.SãBfv&fÀ-µ{íÎ?þÎÞð–wâ È29?`„-œ¯|ëk(ûäá<”AóRË­w¤}ÝtèÑóÇšn?€¿´|Ð7uXä¯ô¬…¶þÌ“0lëïfr1«Uw‡ ]g=L+sÚ£>çó‹ù#Ãòæ 6NÚêÙÞàþã‹fÞýaÌiÏÀɃHÃFHƒ´µó'3ÚsQÈüëÏBÑ×àuç±àó®ÛäÔÀªSn^Yš{9e ÷Äø¶¸ ˜8 Ü,`u ;ÐuÖ]lÛà>>ôv”µ@6s) œl܈–Àçew#@/J/å(Þë÷ÔBØüüý–‘ß £/nÝÙzi]?§€ý!)à7!—pè¶sòc Í’ÈL,HuhÏÈ0º%#ÍL](þë§ôÞ ªyâ‹÷Lâ¢è#ƒô‡¡S±GxE¯9þ¤žqæG–qÜ1ÿÝ@XW;ƒôLʬSfGó¿P†s‹*>Ë3Èzu À ³3 ÏÉ›ç[§ªÈŒ8Èu¶ÀÆú~ŒõW~.ÂbçÚáýCñP,"¿–6lp¹€$]£sÿÒÓãVzÂ? unµ§qórî¹gÛêUvªc²¼úšË9O%Æü*ôÔ9pTòοkÁHßÎBoGåœo8†AR'¦Àûmù©‹qC]…¦ä¿.Ò8¾Y˰†‘_M#&¼GFc;åarzLA™zP_¢ Á¹î¬çÈŒXß7 „PT\h—\ôZûÍo¹ŒX{OwÜþ'[5÷j«\yK‚¯Ûm¿ú½-|Ëël`Ën+„òçŬ= gØ•6sH\Z+å¬ËÇž}®µìn³¸ 5û-Þ8Æš™ºðMmýø8OL‚é#Þ*V£ö &s$"'3M&ŸŒK¾ …LÄø#?ãn’*!€ Ð\œ²fsYÚŠ<ÿªþÀ·r%¦º¢Y÷ ¢É·sªºeßþ)~Pf\¸vÝ"Êh‘ôð§o€ìÓŸb( \íqšú~Éð¯jípʇ™ÝýG´ó6i¤Ü£´èV¹†¦T{/?.„ò5AµLÐw^T‹¥„B¢%ÔÈ‹ê,¥ /$ð)ƒ(€åÓN­±%°f=¯S{Qc»ÎRø8§ H V?Þ € ê1¨|<Þ‚Ú†häÄò¥ˆ—Q/«õªqr­“î¦I7û¸}R§Æ‘/ÜC”2< 3Š™Ýðߎ’ª¯ž ŒAõ…þ2’[<÷‹Po!†Ð›Z@ÉG@½Ý œô»À‘€öZG4Wþ¡ï„ÅSXŸKo@:+Ÿe­uÏá1¸É9 L´E؇»ñ›ù+{Óß@ b°û¸_´ÈvmyÆÎ›[mçž>¦¬°ûXÍõ Á¯¶ç[7Û7]ìTà’<DÒŒîÀç½ÉB$‡mɘ»Úb r¨ß²à¹»Ñ{H÷É¥f*xsÃíô_æFò\ˆrLO6õ9J¥‰Åý´GOËDCóš‰rªÀ·²ÜR(4@¼ã0*¸ðð¯Øäƒïu×A|BéÏß'vômìçS|)ùæðË-î8«Õ¥RëTJ{ݵ¡vtÍ”¶¨v`¢íSïGCpb]Ÿ@Q»uÕ~¾è´îåÐSy^\ £ õT „ÇtJjT: yÈRó0†¢.i¨åJšçôúÑé–Àò£Ö‡Ùy˜iK$–¨XPÊoÅ;S°þt@<Þ@Ýj‘Oí§M ¡ߢWçëo&ÀZ ÷ô†—êÅëê½ VmYŒð¯û{Ð'ÀB„1’óÌνúû2x¯$’ÕÙzè_{Ú>†`0“‘ÒúÇ»K£¦­,ùìî ÄNMâ¢ßcz9þVý%K\u:²<®Ë ¨ý)P÷O£‰ù ×g6ôw ½÷>„tÀÏÝPýå¶vDÅ%D¬^ŽO=ºœÿ¾0E=Â-ù.¸yÂW;öy£Ö ‚ÐC¬¿&˜X}íùçùá½Xþ™¨zòdGpÜjÏWøƒ^J¨HÉ©±ŒùYÆâû-­dó/NY;§–½ ŽÏMÈ|„ÒÒÑde‹.à…»”LZA !rÇÊ¥gÆàè> œî…=Ç;¶ú*¾A@/ÀW8J¥µ Ñ«h9¢¤Sˆ·š+ã¬SG’ÿTKXBòäÑó¬‡ä>Ÿõ³@u…–Î tµÕ~¹§ýð‡1"P/xç$ 5ý®›÷`U“`PꮚQ© ˜N–5aº|p?oÉ™VX¾œìB" ¨^O?½ ¿Lÿ´k¯½Ööha,>ð½ÿÂi­U/ª¶ NêÆØ(¿ŠDòË8¶¼OEÍ6ÄDK+­br³ÅȾWœEåiPñrsgÝ„ z‘ü籞P BðÌÑ×i¯“ÞLOž=Ñ„ò¾Ñ©´éÙý. ”.€–©RµÍ+äL¾¿À/ÿ5f_þÜÀ³|ó[P~ÒIRƒïËKû¾n=TšÅ{;ksÑ¿ÞóÚ.ƒúk}Ï1œ«Âtû5E@B$bùŽ'ͤ‹_H0 ·ôw[ÆÂ'-«Œ5;€šÍ¡/:•7>*6~˜“ƒ m×ðBˆÒíIÏÅ_n¼X.áÄÖ%þ=OXGë¯p{Ô}©9Fg8o"„£†hPôwŒ ¤ÑßDR¢š`?ÌzdìááÁб åe+á+þ'—É R'– ¯£Ï/™÷-.BZ”ÑØQ$ì ä`\ÔÖ]v1 Úï×Á£úÈV@g²•/8Ñ‘BkýV\É tQ,‡‡Ù×í°Ûž·§ØŸÊæl…¦æ6ÛƒëïsW-°â+ηÛÿýGv~õ\Xæx„íµV´{ØökC7 ‡ 5ÂÄÉÅo –%:$T8ijHC^ê}ráñˆ¡˜)i§–<ñD%ó¸ÑÄ–ÂeÖ0©ô“6˜W2ÑѲ{@ߤüeä)%mõ2œy‚à†]R_΋ÒVÎǵÖí ¸†[~ÇðÖ`È#­éoü$lÞôAò¾×lÿöPGB 7/¤Í7-©·azœ?‹ÅWо=Ô^òWÔ)¨µŒâR{°ÒØHJOÃ.øÌDK³¿ %¸±8˶4(~&aÓQ1LEs/Žp¯§3Ù¹yh£Ö oÂݼíg> !‰bF ß#Å4©©‡V/'ÕÃÙÐ(N#‘I·QTb¤\ÎEyTm4•]ˆ¬f?‹ –¶Ìso«>4c “e½ô»ÿ-@-P#H %«§½þÛ´| ¯¸êûtŽDúâ"OuëÆ­W^>Z~œê3 U–À÷cI–¦-Ø~<2Ì^mH 53WcOë[˜‘vã÷¯À‘€Î"˜[5×–/_nßøÆ7ùT ,_›ýá¾ÇìšKO·íuOÚÂS϶y¬ ¥–Úc2ˆùY.Æ5#ûû,„—¯âú°Ç}“FÔ?•ø!£"4œ3Œç$¿!‚ä¼3ÝO)|"Šo¼«¼¬)9«*—"D…•FÈ[îbˆ¦ä]:Z‹c\PQ…o„(>÷<ÇŽ@þªKAÝá<¾}HùßøN³>„wž[à@'¬ ¬ÿÿû‹Vð'EøQZ á)}àM'î%±ù’ä ð) w[éõR ìe¢ŠK[2qÚ’ŽeS)gzöTÿ8"N¦Ž“ÇžX»HœUCú Æ··(º¨MIåÙ¶{ ôìÝ£¹'N µp©ðjÅ¿éƒw”Vx§‡TÑxrŸÞâDb—PJç4ް:¥¿§í­¿ ´N.õµ>™ÎéEÌÒóÿ†%@rS¼Ãr %»»½þ]­uŽ6 ’°V§ú=Œ¨õeºÙŸGYÙ€8'é Ї  šÇ9r=ËÒ¡v½bñ z&ŽîuÁâðÍÍÍvï½÷"è‰0›)P‡ù§^iŸý§ÏÚ¡Z<Ï?‘X S9{` ÓekÊç‘gÄZ9ˆä@=ˆQjÏø}è è…M?OGï£L0/$ùgj:¯b£¢é$O¬¶ýÿöÎ<Èó¿·÷¾K–dK–å-&“Ì C…dˆ«ŒÙ*@Š0ÉL¥ŠT–ÊLÕ,¤â UóS3ÔLAlÆKfŒÍ2؆@dI#ì@°- ˲dYjÉjµz__÷ë÷úõ|¾çþîïýÞÒ- Y²ºõ®Ôï÷ûÝ};çž{î9ç¢S.ul´A|£pÇE `Ä$¯'-ã’ÜÛû7.=ì•à¶ßC8çÎûÛAØI ^ôÿ“? ‚]7qnÿ<«,a½¤•y11 5c¥7¯šù²JüÜ?´Áö÷ÊyzöÌÖç‚ôÎǃ̖}І“Èe´³¿GV„Õºˆ°VK''El{1A œ}ýò<hf©\²À¾~€­B_Ràš|*˜Ÿ}8˜žø:óè@Šª4F \V{2ATr}+loï{ÜǾc¯>4$ay2ð—–þL~vìþo3gA¡øU'†ð’sê6™@FD8Ñ27uêKôÔbצ«DúO6ÕÙ#•é© ”„7àì2€Òï/™Uî÷ã’éç§ Ï–!‡»zÐ1Øù+ÁÂø‹Á[þù¯»9þi¨ËGöïßIèÍŽ%‚|ÿñà÷?ð`pµâO}2è®oû·âxx1(j^´'ƒ_~ÃÕÁÑg^E1‰ 8XI»ÐMµmˆø PpÆ÷ñ]¬–©Æúk49{úÔk>mŽ’§/Â"ûžSHC¦¯¼’³k¬$/Ò~È`Ú£k¶ÙòÓl´$89Êó' „± xä¡ øÊ_‘'mýÄG96$Ó'`~ünN ¾¤}3LÂ+HËÞ_bÅB·Uí]³ÆkÆÈ|“ÔCÄ6Ñ~‚Õ~0Èvƒ©8‡/¸_byr!œf9™ƒÊá+híL`?‚¹P,8’ŸkßÒ.¤¡ËË­P‹Ç%?ÌN<ÍÜ@±G`ÀžiqŠ™¨›c V+"ˆä÷H@}-·j|‹P äR`›Ú2?3úïXìÔ|g.Š­²j.VÆkøs)"5O›!ƒl¦N}nëR÷Àޝá§ÑÑí;  7þN¿´ÃhkïV…DdÅ`FJ±#)‘4íã0òÁEÜ|e0Añ¹{>ÏJ!uã¬)‘g…¸”äbWPè:ËlÿØ'?‰8p!øàÿ}ðÁ?ùÓà(Kßý–~¶9,ÍsíÖ6¶º8d–#7øv‰ªsšò kïgD,†*F¨ò]óçðâ PÑe„[{c<ÇRŸÉp%:TŒ®P·]·ó¸¿p'Éd­1$ p: ”ÀÀÉpˆyž[{$ÂÙ+°‚ék0µz®Š!ÇÀ–kƒ•Ö˜)í52O™É‘™{ ßS"çI¿›[§ŸýÌgP>Üs÷_×ßt36郫û6£(S N¼: ÍŒÁ%çs’¨+8ʱþEõŠ;+]“£6 ÉfY¥žþ3æãb“‡Ú*~„”2 läÛzÔØ9˜C K¤êÒ¹Ávöý²H¹šûû=JJX€JFÙùroìº>¾Epòˆ;%aƒ²fÝ]žÑ¯V{´ma~Õ3ݵ²íGA ²[àê#—>6d»ÂÙÂdÐÿؾ r:˜9£-Š€ŽTp“Èé—GXAˆ]—žN?7ÿkðŒ¾Î8îÃ_Ü|¤ Ø&‚橊Z+wp0ò†ó«¾:Ï÷ŸºŒøU­rþê¥ZbN,³ÅLsÌ7ŠìïüwüY¢^Ô•ßWóR¥|ý4ŸBJl9}ú;Ì€÷w\õ0ÒgáÄ`û”f5±IÔщ&¡ä>!´ä×$¹ xHÒ«Ä„éÄ"ÆÎ7ÜŒx‰0Žô8$—_ÜéŽBÊ1/opäãÿDð‘}3ãW#(t8¸{ÖEŽ%óèÂAîyE7i4ö†ˆ¢Yæì&E®8~žøp—2üZûæåa.ü¬¤ÁCÈQÀßÒ†YtÌèæ§Xýl÷W3üô¦|Æ9)èæù/Á „ù§­?ÓW÷|à |9JYÈ¢ýZ>×v؃1K¡Ph:òK·ÀÔë9”[-=ÊMǼ˜ŽÇÆPžM2/)[Ëf&ªË˜J,W¼žîM=Híq¾O[Û8ª-NsRô#øÏ0ÖˆþR٤ɖqº¬õƒÓR΀¾¶JR×ÉñHá ⸺›É¹ÄKä0 eZ@ÈçžÙ©áÁ›gn¡ú«Ê>^3çx)¯Õ{Í x­²}Mó ‘€$¹¹©á= ào²x”R´F }kK@ ¤à@sžÏ¾¿³{3˜>ÃD’*1{t:¤ôÜÛ}ìeXtóŽ7‚šÙü ¨yHÖPäoÍtÅY'oÿ4ô2eÜög Þ}ë­˜±žÃ‚m_Ð߇¹'ÓU`,~?¢þŸ?z·jÛÓ…TÂ} ¬ù©ÄŠO›ÕR`t ›Ø]DIenXkXq“m´GÀ TaMxÕ›(qv¦÷çÿ±]<&Š)=‚»ÿ2®ë‡ÿ¯Lnµr]hì×VN€–•žËâ­¬t'Òw½ƒHàa ³¡ÚNi¯.ÓÚ)¶)Ð)ÀÕOÃËÑÝ2gžážÆ98še¸’`ão~Êw–ʃÁÔÄÏ8Îûˆ~GÂiÄcªh;d¤áZ}†ú[$Z¨F®ÙÐøjï±(!Æ[æœd±õ"”Øîù™‘ÍL =ÍcÎ^ðåàIê'¦¸\„KF–X¥$ H"›76%” .þ?þs¡ŠàÒEßzQ¢ª XíQd åãJRDÞa; ++àLlוÑx3Ø·¸Õ?Þ_LBèê.Ÿ›Â$>̵ÁÿC ¿ë(«"5râÁ(ÌŽñ$¢[äÌ~˹ƒÀùgò0m Ð$©>6È®=‹>G­ítAúë›»$Ø æ9µIB¢ ŸŸEõ1²è%üÂZûAPÇ øàøÃ+HÀ+R×ø«+ÜÖ°ÒUµ®ÊQ·bïúYË…ìb1Áô@îr¥œàÃä[@xíÑ¥ÂÂØÅ|š3~¦¥_súâqûWkÁzAª¿ÆJK³!Ƚ½ÀÉ»{6_ó‚úÓöUí )$ÛnV†4ïèÞfƒ/Õá4:º¿ÝñY  ²IVVó+vü’M´ácÏ1i u¢!üeuHöä¾û÷›¶^¼éÍ7©©ÜO÷"׊ÃxƒÃ&¦X½P%«Ÿ|Îû,~ë¦mƒ4áLæa§eÓØ²‘1U,ÔzÃ) F^ª»„jD^ÅŽµ»ºí‚à}ÿÒý kK¹pµ7¦¼ýK¶@Æ÷ ¹¨­Ì4òýì×9’ãA*˲ý˜¦Ïæ&—øVý—XÙ9ÊënØÑA KB.gYœÃØKú!Óºžã‹Ìl±õJ¦ÄDÉG|6Á—•,~ ¹9Tìj¸z*„²U¼¹F1ÍÏgç#/37ø[ö€?[,„býìÂÜÄ‹"bÒiÂh!óûL•Ò¨WôEø]O@ÝáFÇ!,‚?Âã=›®~ l»h×Ò"®eç ,T6%™×;p ûX ‹$Å %¢g0ß¼ ^Q¼žLÂl0tt“S2ßÕÛ«D¸Dj›!÷òáÃÜ?ÿŸ$ú…à[ß¼/Øñ·‡‚“Ï r»+›â‘àâ#-ŸÆ£eÉÜÏ*ÞHF‚7¹ºhòP$ߥ“‚‘YÀÐIš Ù›µªÕ%²¬ qé ÷õn`Ç@¹ã@õ„k&H¦(I=öú™îSAóZí˜oKˆ‡|ÅÒB€O\ÂÖ.¤ñ8“/äáÞwBê£}'¾@–FçÑ–pOž{Û{¸zcÞüüa˜®ÏSGöƒÜÈSjˆ‹&Yñ5t~ï*ㄆôŠk ýZ¹ou¢}†ÏX¬Ê«­øa7è±R‚¯'èð9DNeFŠK‹_œ›¾7?7qÌäè"Í#êe ÌYbÿñz=×P?ÙME×efÇ÷¨ïØvãn&³º‚ÀÆ ·à þ99ú2H`‡‘†: ”ñ‡ ‚™…ÊÅ$’Eb©wõ]Éö 58ùòe˜‘qÅ ÉZíü`¿¡£ (21‚wÝü‚ï=y0،ݦ¸$[ Z»ѯ7yE“!̸ö;ô®{4˜;xUæVu>ºÙHQ†Y^<ŠÔ9 |KäêS[L%¿Ú’ñBÿOHbd@.ˆKÏ?¾$³HHö²ý£ }ÝA1?d«» S\ìׯ‚Óh%Ss6+:HE¢ËbÜJîKT+ªN"bIDAT˜ø¥²ðofgžf[ð,[’ÃÄGz¦ž$=ÝÊØ‡ÚâÚ£ßꞨo‡E7´a"ËEÃfn‘ŠÔIÈ}ét"qÙ“ûæ¦ÇîG¨gØñÐ{B€zyÀWB9¥;cÕ,æþY@]¢Î£3@uó§ìÝß1°í†=ìÿ·3{$ÞÇ¡”$‹—lÀõŸÄæ_oÏVé}“J@¤¢ Œ H%O ÕŸÛÌ&é ¶ßøÀSHÑM¿1ð‚W5~äÁ¯asÿס4„Ÿ´¹ª:æ“Æq•~+sAá„Æ'$>–@Ï5œ’„©‰¥79•$Ç7zîRMÀLÓÒ>fæø: 耗²{ö]ºYvXÍ{¡.8»OtŽšÆ] çôùi,%:èw(0¨eÎósÔÈc[¯¥Î><›$¤#-KðnDÊËÒÎ2–—Ê+㈠Ïa?à-AËRwJjµòŽè­ú«Õñw}GÎVzõ >ŠäŸ Ø`hÐKŽ«íØC²ª ðÉà Ú{ŸEÜü!Ì…MÃSR<¾¶bò¥mX‚"¼^n½"õW8tèPsŽ 80vò¥·B <sn¯M¼!f QØC¢¶;:r³T»ØSv0‰Ùs$hÈÆŸ»ä´·—Qk[7F,ߌq‘Ÿ"2:×êÙûâµ\f•…JÂ9:œ˜ r;“™‰Jg-ÃÔƒb©°Uê Ò}'¹7Aœ9Æ[æò–yt%¨{×&,qY«˜pB¾BºÇä"ØRœã1³% ¬ ÁB¤2“ÁäÈHíý „0JωI¨³ !vzüé]yÆZz¦Ê+‰¥³ˆÑÖe`±ßbcÊ^ÂÏnÅgìž),Ì|ÆÞ#KÙA²œ9šNŠxɾµˆŸõŒÔ”˜*B¤úŽŒ½ô6(ïqüwÀRÄ`²hÏ91v UØmH bâÒRvþ–`u'1Ò…b¦9&ÐuèW^÷+ÁðàsÁÔȱ:JÀo 2oƒûÉSOCØÈêííEšN•ˆr¨aVm‹î?µy…s:̨WȇӋ4Û¶ chÊ­,jŸLP}v «¤m á¿ÚÂÈg®‹;¸¯í ŽPÎ1†aªDƒf&ç*ÙV#…ön´ŸW"F±›F”0 cra¶`€IëM!Èu±x?'­gôE»{PBŒvôÉn„5^µ)~Å÷]5¢h™)sþ´ð— ŒŸr¬ø:Ú‹LÅȤìfÑ3I•LŠ€ž¸—öŠvUôXï@ ÑgUH~À±±¡C·ôo½þ{éLö&ac‚Ù]*žDu PDHM2öü-\äa*ŸÊD«‰”?xÓ*f·» G¾ˆdß–o4æá8Ç„kmdtt»Ýý]0Å“42‘<åܤuïþ{ÕÙX‰vo sõ©dY±Áš!×J€Ø0rMa:2Õjoœ|Z’Ìô›n‚˜ÑξS‘¾Ä ˆìágQÉÍ€TQ¯–A ááw) ûk//ûùÙVÉå'ˆG%R2¨òc“0ã¶8¨š™ŸHpŒÇ»Ž Ý(û³ûJÅ}V|jꯄÈ\ÅО™oGyhHy_öÄÁžÂâì§æ§Oï…š”‡€ LéÉûV¯N]Ù¯¯ÇF@êAu¸GiÀÉñ¡Cocé± %ªk)`‡ûùñ‘#ÁÀ70qµß”…˜œm dlDã+Þòc(½h¯pLøË¬Z=q"‹³£zu×økÅ WW¾lÒEÓÁ¿¸x >oG–>W—Wõ—ùi¹fåO±‡¤0™–„„^õ@Ýd«¦(&‘Ø+\)F›ÚŽý×"<ÕÍjšmKÛìïyÇF^;+|™Õ¾ˆ4dj —œDÆ„t$IØÂå óã˜Òî)tѧH÷•ƂɓïàþK2ß±‘øá‹ƒ,¿@»–žÚ¬ .®ë‡Ø»óðú¬`Ø­@Âë(/Õcx„‰?÷i¶€Oa®R}‘bÜ%% çÏ2òÚ ók˼´¾7 P¯ªó…J P£Š§` ¾³Û  %ð&´¯dA5d,išTð§=};0êÑk<2Ô^D°Â™¹+³%!RA‹.‚è‡ ¾Áš²âø©àŸUãÞг*Æê~®C-áágïê¹Â-¶+Å,üŠE¨N'DEõÔ{L`ÇTÕa¸åú'¹$ãTбr9û,— f9q]fîgô9¤ð‹'Ö‰àÎ<çøª•Ž POm\¨*þ‹X2ILmOž>Àöh ¿ƒ K(!Ù·åŠúbR]ðÓ_¼}ò·uÕöž'rÃæYøÌ,¦{¹D23ƒ¬þ ³cw#¶û¢Èzþ0×%ÛSf¤Sd¢œŒ†%¹(—öïFBêi D ŒB²ÿFÿÖ¾›ÎæÞì€aoâ @`ò61v”æö\nÏ$±íýáD±’i"+À#‹øi:‹P-̬TŠw tòð?Ø WËtTUEEüÎÏ«3$%Zí"W™“>­kj"‰Œ”f ÙeöìRâ7G&|H|g=Š<—çìþtоC-€a+<Ô¨Alê’¨‰¤ç°"¢Õºƒ+}eD³° Uþ ¦µeÓ.ƒF×>в¦ƒ]½©géëØ. ó/~š‰ÚE4¾Aȱ#£®–êMïkME´~Pˆ’û,JŒ•Èy1‹e‘gã)÷#¸s_~ž3üe]uêÎð~”‡×ú|5Bn£!µÉ9¤RÈûOÀ \÷mö¥7³Ê0ó<àM)Xº&ÇC‚>]JoŒ+”ƒ`Š“ÏŽ.¿“*KcHn…Ón3 vüÂ[‚‡9¢BÑFÛ W4¿Z1”ïq59Ó‡¼¿ŸÛ±Âñ*agEò÷Ò­ÏѦü˜õ3gý†#¼>Î从©.îƒèI˜êmmeE_ä9¶ó0¢ª£»eTrKØÐ^^8„JÅY¨Ýl£USçýÓÁèÐwŒ›¿ÌUYrêþd éK#óÙPuÇ+Q¨k‡o÷ñ­’qO½û¬,€GŠ…ÙY ;ÉsÈ“Rë/qn×ÔØàWP8šf®hÎè(O£.&`Xœ­*0^bT•õü²Q€ÆÄ¦Dˆ’Ë,ã§Ž¼JàaŒH¼ ,¯s?,hBjU›GŸ_’=[‚´è,ß3YF˜ :ÌÀàZ‚á•Cž`ûuÿ,8uô9¬Çž´¹`ÇUoÓÆªaÞgÿ£„ñ¹M;MâèÃå~º®gèx¸ìàÜK4"=¤:žÇÊñ‘æ„­5ø-@¿(C’¥g„¤][7zVôI[‡ÚB´÷pS2ÔÃÜ E„v¤''§jÛ‰1Íá`lx?Ï£šû2í3´ ¢‹Ê€LmÅW½´ê²åPé3t¾ö5-ôÁáS¡a*ŸÀǨ$Tƒêè0Ì<Íõ¬™{KÏ.ÌOÝ”ç#ñä¡ üf ÁQ^|0|a㹑€FÈf `·‹@ ÆO~ß–k¿ Ð{øà¤$ ~É?ÝËÞ?°•yäÏÙ¸Bc,3ðu«m>;Ö*[w½Ñ„‹¤Mh“»2Ïi¦X2ýXÍÃçYäP]\õ—ê#C—"½eϰˆ–]ûfVðž—Xé9ŠKõÈE.Ç`Û“C(bÌYdðg''t„‡Ø:‘ÕG?àV[Ñ9]Õ‰Çbþ8ç÷0õ–`îå_%‚×½‡ÍH?:ŠÃà1l‰«Ÿ|¼«­±÷¯zFd>±ë„@êp Y3RâBJŸ¡B“å"¼s;H~7À¯==rZ‰V|¸–"‹ä„}U³×è…°µÐø§Uëýlt nÓTi'$€¦ï&¤^þmÀWs­ÿ ×²R,õê|K2'QºZ©Ù=Âîb^HfÀe(òY'b°B›®zE%ƒéñc¾Xžgë”k8õT‚æ œÍE÷ê~ë|$ؼõz‚2ºë²"T”ŸN…E@›2Q¹ Cº¥xWH!¿. [ÂÏ~øÜ,ŒÍE?ö1¯(7üª½«¿\<á<™Es[ð€°‹³ydõg¥éÈJ}qk<ÚùYŒ©$9&$r >‡€aþ ˜û '"ð:(*aJ9àMœrgÿtHØ…•ºèÍ7#ªûZ/>²V² S0>æçd9TI>—?gy'™že[òÐäØñÏd“¥Ÿ}a?÷?ô¤u‚ R—"= «RÃÖªÚÅð·s‰‰Ü…@—PjÀ™^r‘†¥,”Àôo¹n Jà_#º"ÏVÇ%„X†ÙÛl¾‹º 2›84&h® ÓUR–#+`+CE´ÙÎÝ©jV¹s¾qnacêãWE·½0f›‚(p:ÝôrUZŠ /d31Û–D ‰DKT—kÄh[*ÓIœ%€þh0rŒûò‡è6È|òI¢'¡XûA£;J³ã4×µUå»6É˵°&Ð>5±":ZX±Bôà ÁkÙÙãø…íØ(+>ª¸÷ò“ƒ0}ƒLgg¦«»ЧÓÎðCò%5*¥Qå6´ßå†4˜áÀÛC{ÅìøéÃÿ¦gÓÎÆ$ÿÊn‡t§Dý²+9¶ P `MXwÍá%ˆQ¨KHeVl‰ÛwÄ;ÐìÂÊ£!ˆ¢‡çH¾Y­ôNs_[U<ªºûPíj½ÂøÃr$?¨¶½}«ü¬qù1¶b"Ðe$Èfu5“#ÈæÏòЉgs¬…H¸…V2èhnA‹²'–Šq¸"Ú¼]kÎU¢7üµ5­ýŽø€50öÒÙ+؞܈I-¶*ˆB,-L@´°ÿï‚ ØÁ¾þ(Wˆ= 2; ôU2ÕêŽ~F7´ÑKŸI)ÇîŸ œmÕ]‰mHÀŠòŸt&Åó᳄|ç"ÓÃ×)Cê{wç@;=|xv}œìCš‘åðÙ8ú­¢˜TàzsM`ÌT9s\Íô™1ÅÖŽ¾°’ĶBŽ?( ¿ÑS‡¹g{iÎÍÅä|[‚2)žÔõÔ¨ÎFÍ&¸g<¯6=4ÇÝl¯€ÓjqCÿ†zRɾ¤ÒÙdò %$øtK’LqµqÇ÷é\†¹|„½ÿó?†ª~ůN®}…|ÂHFL±=+IF_zø4£üÃÅüê¸#{°û ŒD¥Ü>a „wª h~5î&pýb3T (n™õ¿0¹–»ú¶þ×j$`Ôf§¦ßøèÈhÀ¡gœp]ÃÍœÅiµrÛM?§åëAC~q[ýdø(Œë“Ä£ëÝâUrtÁµ‘E‰è®C”wZwí»ÐÑƾÁ1˜z?åØï¸š ¿B 9ìï¡r*‹( ´ìôS[_Wš~kKŒB ÐIç“:¯Izºæ+ix”'Ž>€¯Û®”•ß|9«h"€J7¹™ÞG83~â¿iãÛÕå‡ÉW¥@dIˆ-Òzrü„N†JØ£_6K¸êÖhæó^ Dµß®J¡ÿk¯~\¤!•,x¢V$ÚÜÚÝÐÎöùÏ=‚ù—„6P3ñcÕWE"(°>#{ºûÖż+¯ñÀª||”ZÀ÷¶ôÅÑOHj3ü¥GçgN݉–áÓÊÂAÿ ð½¾u‚o°}øÜ›Ï³ï&¨î+›Û¬*¶ÿG¶ÿè$ð©˜aU !itt¯@úºrÌéHIŽ]æ`ÂÏWeQ=g«¿Â"zÏ&Œê5 Üvá%¾¸÷¶¿— .å;R_iTWWÃjÀw¹êW±ÂæYS’ù¹B¢Àè% ðYY#ÐÁwzø|¤*gŠÅŇò3£w s–æH'3ï 1®gtq-+KÌ[ma9ÍÇÙö@Ô÷”&•΋ ùv;ß ]WÞ ‡–…≈Î윚:‰~@)èãþ™©6B¢ùïRÄ竟Ã.$úŠG‰³æûQY<Ö5YúŠRŽJ•Szÿnщê…Ô¾¬YaeÁŸ•ÚÒ7•[ÎðSc°[¾07uúžüüø )N‚=D–Vž’[yJ¬·5 ³˜ÍŸ³ê&hÜMš`q$ps®ØÕÕÿ‚ˆ#‹§,t Éü,7Þ`½}ר‘a5P9T`ÓßʬžÃÑ—ŸâŠ·4áOÙ{úˆþéýýSTÛgÓn$Ë\U>ÖZpUWd%‘z*Ì/î©w+@…‡ê\Þ·ã‘”O ¼sÏâüäýX >-ñiœ{ÈP¦¹´å7ç¶f5|äæóì{ ‰Vï+M6Zg'Oýo<ŠÝ}WÝ<€™"W8a< ¥¹¼bgG0 jX‚%ò‰c¹4aEjä<8U…ûª4J ?ÂøJ#¸ôH Š^APŠâ‹ˆ‚í%âßyê5röí}6±3|å•b*ì.®|SaÜàiÅ ðW³¥_UBTTóå¼{ ‰ÖîB›È  ìQ[æ&O}¶ÿR÷Àö/³:éÜOæ¢5ÉÅn7YÙÌÛ5ÕV‘‰q­b\ĵ ¬¦µãVCEõW]J æÇ*ÁOƒè®–a”XQT‹}9DÅ3ý(–Ϫʖ>ýøl!?}ÇÜÌÈ#0÷d“LñÖ-}ê¼a\œy(5©±àx؃ˆµ$°ã!£©-+Md›üx1µouŽ þû¯ÐÓÞÏ?«"Ež>EÅ#ʺ*o•§Õ0^ågžUÅ®’ÚyW’Ä¢…µqˆPŠÀй¼æVx†¿ò·æÈ¤öcþ _L?Â%£ß<ÃW§½N®‰Î®ãíüÙ!d Â(ß$Y $ðuGN=dû{:;–±`¤ŽäŽ…Ÿík„)ê<çT-þQ!û•0R—‘à‰"U¾%§ÐY …wP‘ ‚¿Ö>Hóÿ±âkƒ/^?TǨ…(纲›¬šàì»6DR6$ð-&üû¹Mø›@²³â²"ÐD¶•ÐeÎë^,È-¼•Xæ=W«áa”E;S"YñØŠWÎûúgUÉà½g©ð½ðŽöð"ç‘›x´PÈß™Ÿ}JV”q)ä'1GäÎðÃ,Uy¹(g÷Ùü½˜=ÐDçÖÛ`EÏq^½ˆü-®'@G$ ó?7©ý¯)Aó]Àó6¢a-8ÃÖŠË®þU…¹ÄUÅF¾¼ø‹V[Päçèë _fµôúéY¤!¿R˜ŸùÜìÄЕG˜ÙÒ'w;ÃUAÖ‚WÓ]ìh"€sïq·ÒËÐ3Ò³ß}/êÄ…"JõœJù~-$ EÔo ª—ôUjSÎÊÛ¹À‹ëS(ÿ^)°ÆÇ>£Ÿ àcþÐfÕ÷8’£\µv~fì^˜ŸÇd2Ü%Ê+ggøa¾Øš‚*5h¾]üh"€Ÿ¯Ï… ™•JæPKÝËj÷ž¾Í×|$Ðûö» H{¨‘[ ð#‰^äàá©A¼,iãô }«<ÃÄ1Æ9†¶ôÝEàx•;îð?Ï9¾ôðU!íÿ•Ø pð”ó­*Á5_ïh"€Ÿ4¡™Üå‚H`€àÉÑ¡¥wp3ñc|@ èfPºxƒóuär!—sϬú Nɪ*À>Tc`…à#Ë'ÄæÚ,ÌŸ¡1y©Ï-LO>8?;6…ðŽâi 3|äÓœüõy8ïæï¥ÔMp~£¡É­ó) e‹…ùg&O¿ò®ÞÍ×ìfO¼‡@8¸·s/9¥è¥>Ê¢>fÌ'zµ—à‹âG·ÌUB²¥/£+É}…ùéOs†ÿ0Ö‚óC+´ÿ·+³åE¹×W²és©ô€VMw~= ‰Ĭ˜t çÝÏŽr)) ±“¬Š X)Ö³áxyðO•«÷ÚoùËÓ‡…Ïx4‹â¢xÕ1ª¿ˆG<(lÁŸÞݪ/ ᆖe€ƒMb/Gy¿5qúȯNŽ{€ã<©àbKߘ~$é¤Ü]N¾^Íç%ßMðÚ ‘&¾Ä†9OdY!÷sáÛ@ƒ0 Af,Àˆ+±T|;/‡9ôîàË%¯úUÎqàn“Œ:°\݇€Þ_U·ÛsÄÛ@+/#Z»ÙÖ¼câôÑ·NŽû¶;Ç7=|eÀT+Õ·KþM·Îz ‰^» F d@/ÞÊ~ù|@Ôð<0(èUuá]PªÓ÷¢¦RÆñÌàæc=÷@\˜Ñ  ”vÀâÐèÉ—8ع;×ÚýO ³Mœø'žRY~‰­&,;²ô±\¥+_Uá|Xˆ'ó9¦‡/E¥ò Äuÿ‚ÕþóÜ™‡¾p‚ÓÃ'¬©‡¯Þ¸L\\¸6¨ ˆ—W sÅØ;»û¯új[×ÀÛ¸O@’„Ú„0o:â…Jå WT>¼ ‘Øe„³ƒ‡% MÆrFKàGùÜ…ÑÓ ³Ó '!©Z=|—ØaÃ$–]ógCö@\Øa5$@¦ ÇÖrjtð]\[u{gÏ–?u†Eìæ ÆA<6ýO€ªÀ=h_Ñp7î?+@¯Ñˆö÷ˆà–¥u§Ûsä¹ãÉÏÀÜ{¸X\˜W!øÎm!FêàËQ›€O'\î¢!ëOÿ¼:7l£¿“JP¶@f±3x¯ûºú°3¸²ÜÀê>+m´7·ŽÒO‘8 réÈ(@%ðÆÀ‰C/Á€>ÙJ^E˜ŽOÀÅ¿SzøhåIqGê¸-lñu\©øäfÎ2PnëÝ…mR{Ô¶õÞœ ^ÿ‹†4 rzú÷ ÞºK¤Ú«ÆÛl÷Üìäð—óóÓ¸Iøsélë›`ÀÁ´>* 5¯ËïݳH…Ùw Vx![¶y5 '+z’¾ü2ÛÿÃ%ß(.Îý†J§8^¿Î–þFßÿ¤íM·J\pɸ‹2ÚÚÚ¸5·ÈÕÚÙË„}ï‘€N2lž)/MþZ®­õæL®ãÙ§¿IÛ »¥™ˆÚ 8xg9A°üs¹6 æXÒú`e ¦Þw±¶ÃþÈßò\Lpai.Ë­¹Év1«½,nÒ’‡YX>æG@/ÑåR©hÎ5ÝÚ=pÁ€€^. 8½k`.cÌìN@¨ŽÉ,ÆËÓ“ã{NþVv©Ö÷Éö›àü"u³y Ø3±'ïÔJ©ø2Hà(÷‘q+ÉÜ þ?æoˆ?ƒpžºÎLñõ'þƒÜ†|×D÷+ŠIsLs®éÖî †üÊË-·þð‡ƒÞÞÞ P(`>Û_™µvÅ6x¨—PS3LØyÇQ"ºWÆDEá©#•Îmεth?Ëå}É…ââüûúeÝ=¨E•ß–rñÉâa¿Ð#Yy™Êóȇ×íÔäeîhmm5Šsc·öüZ§Ix~94SŸOøÛ‚íçÉPßñÕ;^†ÒC(®œâŠš0ž€ïÑ5@ÇWšã˜I`Þò]5Ÿ‘Ÿ˜–;V! ÏA'ô@§¦{Nº\..Æ…GÖÃ_!ÁÞ7ÚëÿsuV©§$žBlW=}ñi€”©;® ÞFùð)ßAÜñ<â.&ˆXax–ã,Ã38Sê(b–‹¤×µ*â%Äý31ùl ó‚µ#O-êºÌzQvíšaÒXºOPÿÏ5o6Zñzñéòæ&â»Õ^wÇÔ®k¹IÄ/#¾æø&ñ½Æ\%x/@ò™š{¤ÂùÉ7ëSï Þ‰¸jø…©P¾hÍ”&¸mryÎ>ª†œkº7Š=ƒߪÓB‘ç#@•fs¬_ˆ{ë±Ð¿0î-LæZ~ë£%îGpßÓÆËˆ{èÚêÏYX¦f^åþ…+Ž_sÖ-³Tä>‰D½ Æ@îקƸ-9àã!r[2]3ŽBþ’c³ˆ¹‘ónC­„œš›Ës?ä>µ*¡ÏÔ®ª–e½D|Ž%4 `à î:X°2¨‡ ¾pQSL”PÔR”‰§aeíyå€ÃqĘ ¬×™5FiÏáî„›t…ìÇç )’Cd˜Œ€LÞ$o‘Ã$‹ÒrpÓ¶‹ÏbÝÙôó>4Ð+ãƒÌ¹žF_ï¬{ÒЯô÷kû‘œi+ŸxÀô˜ñú¯.ý°+ò±B.¼{³ëêL<©¿©Û©õÔ î«©µˆ‘ú=µ†¿UHcnfÑ<>F‡Ë ^Ãe||ÐpÿyvŒ·%bÍ:×iX'襇%8ÛI•ß”?•å å¼rw[—ÛvIøTøVøQøNø^ødá’pYøI¸"|#\ŒÕãçcóìƒz[Õ2M»^S0¥Œ´[zIÊJ/H¯HÅÈŸÔ- IcÒÔìÞ<·x¼x-œÀ½ÕÕö±8¯‚ZNxA‡-8³mþCkÒK†HaÛÔ³Yn1Äœ˜Ó ‹{ÅqHg¸•Ÿ¸u#¸ç¶Lþ˜ hŒ¯s ˜:6«Ìz!Ðy@}zÚgí¨íœqÙº/ïS”×å4~ª¨\°ôÁ~Y3M9Py²K=ê.Ðê °ï ¿¢¨Á÷-±óz$óß8ôÞY7"Ùtàk ûµHÖ‡wⳟ\8 7Ü…ðÎO$~ðjû÷ñ]¼›n5›ð¾êø`ããfóŸåfsãKô¿pÉüYqxµá†@IDATxì½ Œ&Iv˜—ÿ_÷Õ÷=3}ÌìÌ.gw†;»;»« ŠKsµ$Mk™iæM ‚Ó€! 6†d´e0EË– Û°EX¶lË `Ú”dP¦$îÁ=gïÙcvfúœ¾»ºªë®úßË|YYÕÕsôtOwugtÿ‘/^¼ˆxïÅ‹#;ý~¿h]Ë–-Z´h9ðpq ûpU·­mË–-Z´h9 ï&:ÎÝ$ßÒn9Ðr`V?ü‹S€òÓŸåw•ßôàþ~¿uZ îÖ ïpu:w³µ À;Üšmv%ú#TüOóûsü>Ìo¿~®ïMóû¿ßá÷¿¡\ÇoÝ»9~? ,j«ñ:h€×aNÕrà~çÂÿiÊø[ü~ò ʪàüþ¿ßo-oÀ­mÝ*Û¤¡îÓb¶ À}Ú0m±Z¼üîßùwøý&?güoÖ-ƒøÛüþ J€áÖmc´ À6n¼û èí&Àû Ú"´¸ üyÒü×üÞŠð7›a~ÿ!¿ÿ%bH@ëZ´x89ÐZÎvok½9€àþÅÿ‡üÆÞF5ÜðüþÓv9àmpñ'm-÷¸¶yö­`›7`[ü‡‹güƒßÛþ2Í#:‰ßó>´®å@ˇ­ððµy[ãíÍ_¢øï¾CUðôÀ_E©hÏëÞ!†¶dZl'´ Àvj­¶¬5ÔÞÛáÆ¿;)° zwJ¡x¨Û§­|ËíÆVØn-Ö–÷aæÀQ*ÿCw˜.%¨´®å@ˇŒ­ð5x[ÝmÍã”~ò.Ôà£wfK²å@Ëûœ­pŸ7P[¼– k„ïdð8Ë íXp'9ÚÒj9° 8оôÛ ‘Ú"¶¨8pà.qBºíXp—˜Û’m9p¿r }éï×–iËÕràf\¾tG w„JK¤å@ËmÅVØVÍÕö!çÀ©»Tÿs\´z—h·d[´¸O9Ð*÷iôÅj9°¾l~ øÛ}éíhÓ·h9°ý8Ð*Û¯ÍÚ?¼8KÕ_¾ÃÕ÷+p‡i¶äZ´Øh€mÐHm[ÈÌô+xçsã%è}íÓlɵh9° 8Ð*Û ‘Ú"¶hpà&üjãùíýÿu‹¹·C¤MÛr åÀöä@«lÏvkKýra}ªÿ¿¥;À‚?‚Æß»tZ-ZlC´ À6l´¶È=þO8ð×ø¹$p»î;$ü7Q(î„"q»ehÓµh9p9Ð*÷ùmÖ-n‡mìý&¿¿ÌïvÎð‘t?Óø­k9Ðrà!å@«<¤ ßV{{sáíìÿ¿â÷)~ß~“µñáßä÷ ÒïM¦iÑZ´x@9Ðé÷ûw­jÎüjé]+fK¸åÀ¶æ÷øOQ·.þj1:¼³˜[, ·÷ ¡ß÷x¿–‹b`ðÝÿè×+åa[×¹-|É»9~·<~ð9Ð*~·5|8ðÌ3Ï ½ðìó_)ž|â}Å¥+ì`•à/ÅÌ2<¿ü¯žùÚÿú°â¡ªb«ã@«Üg Ò§åÀíp€ÙýÈòòr±wl¼Cèï,®r"`ÙOr£ Xë÷ÖVW‡o‡v›¦å@Ë“­ð`¶k[«‡Œ³33£\ü3ƺŸï,cçË€;Q.¢Ìv;Ã?½Ö}ÈØÒV·å@Ë×á@«¼sÚ¨–Û…Ï>25Í%@gçnË ‹Åš+FXÿw)`GÑéþþ@h»Ô§-gË–wŸ­p÷yÜæÐrà®sà¥kWdž˜éw¹pçøîó\4Çó¿kbä{íïzK´´Ø>h€íÓVmI[Ü’‡ÇÆÇîÝWLìØQ\]Ybæß)†¹`€ß{n°ð鬶›oÉÁ6¢åÀÃÇVxøÚ¼­ñÈ+³3“ÓÓ׊îµkÅ2G8x-Öþ»Å*ûv;Þ/v>€Uo«Ôr åÀmr Un“qm²–÷&FGGgÙø·::ÆUÀKÅ ×w8 ÐUÀ0oa»Ýv à~j´¶,-î1Ú‹Aîq´Ù·¸Ø36¾£ÇE@=6ÿ p pŒÙÿ(›ÿ8úWL èNw;Sw"¯–FË–Z ÀƒÑŽm-rœ™™˜\Z)f8úWp л‹°+À {\8Üð‹€­k9Ðr å@p UÚŽÐrààÀ¾Ý{F¯½¢XY-†øKüXûå4À(Âÿ}CÅkk«{€ª¶Uh9Ðràq Uî#[2-î%®_?ÄE@S¬÷߸zµpãßÀ`1ÙëWQVWWЧ‹n»p/©Í»åÀ}ÆvÀ}Ö mqZlÅãÇw^zþGÇÿåÙK»ÿÚÌòá¿°V|üöÿ¥ÇúÅ õ‹}ãE19Ûï îwŠk0ã¤ÇгúÓCÅ ùe€£ƒÃE‡+€ß×,.ý*V<úGÜfþ3ËK¤íû×úÅ‹ýµbjh°˜XZ ËÀwQÆ 3ˆ¢> Ý+èëJø9, W]Jè¯õö; ƒýþ•Ï ß²ßùFz½ÿïW–»?8ñ¥1Û¶ß½ç@«Üû6ØÎ%h€íÜzmÙ·~ù—yøO¾zæð þG††?‰üýØÞ^ÿ‰¹¢7øÂþ ·ƒw`¿„P> l ¹‚}ýsÌäW4ñ#œ¯öŠCC#Åx¯ö֊Ǒ܋XvÜýb™ã€®ÿ{ pŒ/v1,÷ŠÞð`qÁ?Eø<âÞ«¿;:X¬q‚à[૸%¼ƒßó Mž…+´/öÚÞ=aa'ˆGvÇ |!vú’˜ï•ÁNñT¿ü¯ô‹ëÃ|úåÕb+Ài ,&~…ùEòð”À<|µ³V€Öey”«(è Ÿí®QÆ"ÖUÒè^æGMâŸÿëÐ;dAdœ›&‰•&ŠÅÕ]ýâwQHþÆßÞyäÅÁòØrغ;ÍV¸Ó}¸èµ ÀÃÕÞmmïžþù‰ÉÉÉŸÜ;0ð—8‚÷ünwxÿêZl¶ÃôÞÅ<¿—+zo¿BýBó¿Yûa|g÷³øO±†–Ùú ëU„ô>fÿÜí‡5 _<ƒ ÞËü½ç¹ ¤¤µ ÜÈ×A);éG‘€–³ÿE`£ÐÐüQë¸æõ¥nuþn±@Ýü7EšËt²(^DØ‹µÀÛw’Ëø§#·2ÏipvýçwP<€ù9b¿6t% Eñ$ùžF?`OÁ—ÞÓéþÖw‹þ?úìg?«a£uwˆ­p‡ù’i€‡´áÛjßüê¯þêàk/~bçôõß@>³‹|nä;ˆðG *’À³3åý„/㻨¯©y[\bvî1½§ø¹óÞÝû³à¬ùn¤ñ̦ç0³Ÿ@Úþ1BûýÌöý´ï>¥?n!«y~­–º—{ìœ ÖþWÉ{€=küí3¤ef^|e`á þÒ\#ñ»{ù¥œ øQþ~ œ#ïëäóàœ (NBÃßà°P\ Ý,Ï[ ¥A+ÆžŸéwú¯tûß;W¿Åw‹þ–̲uo“­ð6ø'o€‡¼´Õ¿}üìÏþìá}ƒÿåüÂü/\ït†'˜e»¯Ç™ü«Ìâí»KaxÁØ#þÃæ/ LßÃùýnçÛ92Z /-cšï!Œ¹²—øW ?‡ðwsßQÒr°îä±ýxîF¡Í ~ñ#à-¯¡4ÏŠmgåG¸þ÷|µâÄ€'®AG Àuâð ñTRœµƒemÿ®ó»‹O%ãùp»@ñ"øîW°ïÂÿ>þ“ø{¡{ß»tgH¿ ˜JÆ2tnàÏn'êÈžÕ=k½O_èþÅÁ>ýéO— #uûç­r UÞ*ÇZü&Z É6ÜràMpàÙgŸ|×SOýüÈââ__YY}ìî½ÌÀ¿ƒpv6ïF»žµÓ;Ó_d·ÿ*‚ñ ùòÚJñ³âKG×ý—ðǸ·–«{'Àé{i´ïQx„yh?…d玀˜‘O’f~y¥'Eh"NÁ?Fz—_Á_‚ަz÷ì -^±Œ°ù û,Ÿro€xôDÀ·±XÖ+à» ¡²ñ usy½,DÄ>-ò}ä'½ÓÐ[!-‡ÜØHÜpx ,¿M ¥âd7=üÍ+W¯þ­óçÏÏÌÎ̸?²=JÞŠk€·Â­w3Z`3GÚ疯ßú©Ÿ:±kxä·WÖV?Éì~pï¹ýÌî5ýw|SÿUfÛ‹„Ä3¬³ïUƒw¸î8—åqáS͈Ôö BòÛ]…¦gñ?ˆ½<:RìDPÈzp+à JC—ÛþŽ®@„ÓÊâò€ÿ†ˆŸt ù߀¦\é¹â448ÏÏ=\úÃ%CKÌàµ\¶ ˜&|gíæ¯¢"•’‹à®’Ç\äåiI¢´ø#8‰ï1C—LøÔ§>5Œ ÿ·¹Þú×Çææ÷_g‡ý>„ìe6öyÙŽæýq9†ûb ¡|Á{Ó»B^ ÁAâü*ÒŒ~qŠý{Ù8O:ÏèîCk ø8Ç[&_ï‘ÞU”øv‘^K¦yO ¸˜>½,\@qð˜Þ²RR*̇Ynàk@±ÙÏõ|—”Ýžñ™M‚#ä+-…²Š‚:Ê10<û¯à€¯á¾yzyéÆÑ\*¶tUÆøy‰‘ûX÷%ŒÝø¯AÏ•-":÷F8í¿F½]òÐù×òX/±ØÐØÃš±ÀQÈ…¥~¡w~[¤×VVVÎãŸEix ÿy_Åš0½077?sýº¤hE¡UháÖÝ6Zà¶Y×&ܦP¾(ƒ?òÈðÈè(—ÜuG±NwƉœä&Þ8š?…a9?8ypïÞ÷ïù¹îZ¯«@c6?‡Ðõ*]?¸3¤šÄ\Yú"ÏÜüÂ}%¡‹©^¾ÂNs½çþÝÝ\‹ u;¹žwÿ t=ß?…@u×?ÂŽSÅKÌà=Úç®þãÐp:<Ï,|?‚›û»I»!ï·:Àvq¹P˜¦ø=à^åw€ò)à=ãa69N'H ¸—yöÿ)ð^Eáx/ÊËwYP˜{ €©yñ8yÍA·8¤ñDÂ"4Ö(óp­)¦ÖŽy~Z¬Ãø~‘P'A0¾BÌ“ (xÑu”¯–G5@…B~¹l i”*‚~‰ B†bß•9”©EêvmEAe¡RTN­®®¾†à¼2??}eyyæâùó¡×·í”…V ÕZwÛh€Ûf]›ðpÀ‰a±cçΑñ‰‰!'Ùºƒñ©á‘‘±ååå=|ÕŽÉgŸÓxãý^o7³ö=àì2°)væïZÐ =Ézõ›ÝF1Û ñǘ13ÁçVN×)(c›:BéøäT109Q\_(O° óóÜ«ÏÚ=3ë}àÜábž>¶Ãm{PÐÝ ¼cx˜kw—‹ipœíO·BšÑ±±bø^„ú Q“»•G°/“Ö¼Wðúyßobz’K´¸¦ïÞ×éß…Äu­›)«q¤:0>_Ñ@ïá«Fƒ"SôÉû:e¸ÎéíÇ„/›ãhßYÂnô;÷‹˜—{®îR-+…7 b $w|öØã§PH&Fž!NeB¡µ“xKàFÉZˆ«ÂÑ¡œ¥bUÒC ‹ŽÞvHÎá†)›Ö–%ÒS‘áö‰Qâù×2Ô²@ßg¾oУ}¹Â ?O?¹†’0MøWŸ£ÝNcÕ9 ÚE”…Ë”éòìõë3—.\Pñwß( 5ï(TëZ¼U´ À[åX‹ÿV9#òàÎ]»†§vìGX3þöÙ¼Þ™F~õÎÜ9ÓQXïÜÓ˜äHÝ2ÚÁóäh¯¿{ypÀ å$ÝMõN™­w†IãØÏv67¸wºn<+Å4œ;ÒÙ#lAn0‰qÄC/Ì×Ò0¼ a?Îö „5*GŒü}½»äw#Äæó\Ê…éÜóþ\ÊÃo˜^ƒ¶eZDèªYˆïÒÁ3ôkÒ@¨Þ@¸b½g€ª€ˆÒ¼Ê)‚ÝøyþþѲ ̶K¡=˳»þ‡QdFÅa 𣸖ÃiE d0=t=Û¯Õæc¥(¥˜¼Ù…b–ïȃI誈œ$n.Œ||õº7¨“_T›ºŒ/ß4ë+½’Xw!=ŽpöŒ#³ï¸$Èå€x9Nz•otf¯2 c áŦÈEù‰âD’2ž¿R/E~™OSÐ{ç¼fgPySB‡‡ŸN«ƒpÛÅåÛEgضтaÚe2öÔÅ<€_=–nÁY‚¯0XõYC¿ëŸE9À¨Ò×¢pV+ ¨Æ–i–æ®]¹"Y³+ Làn¸›úóÝȤ¥ùÀr Uئ}Û«†ßbðèñムrÃÜt3‚Ùxdxhh á8Ex³Ê)F,Ѓ.=ïq–Íúõ~fŽ“ÝÝlbãë²],Ø÷႞Si1ëdÄ$ޏ`wÄWàÇírøp>ë—fà›ëåà­  ' Ï9ò qʯiÙÁ_¸£³Î¸È;žÊ°³e€=»w»wíŽÙ¾ùO ¨4+ÎPä (¹V—³ã±‰‰Hƒ5¢Xæ3¼ÎöaBv9fë høÂj:ΦcWtwñ<Œ"àѽSÌü=zç¿Lüãøóœ(Ppíîå;JI7ÛyüPeÇk†W…SV÷ ¸_þhaÀ{|Öäïm„º«xä! ÎÊ]<ï#Y…á*ËÒ³¾. SιŠö4yyäOž»¯@k„W/òS¨Z_göòП'„« x\ÐûLk]¤X8(/¼…¥Ævò ^VT¶™]v™+{)JEHŶ¶ý¤cz†³ÍUÐÌGçÝ *s'¬è~KÎç#HPÛè´ø\”4å‰V*Á]Œ¡3ÍõÖÖÜÔøù\Aa8˜ÏgáÁ%”…kÓ×®Í_¿vMÈE, Dà­8ùÙº–·ËV¸]ÎÝßé³—;~œ}b,›=,å£ê ÀqúNLèß=48Äwgúéî°ßl@ë¬8XŸ;»˜]ŽcvVæ ´»ÎÚ•CÌŠÙåŽõ|}xt&Z i&‡&c=ƒžðæ€eœ8oÎÚy¬Ö äWåc¥` à ¬Ú%¾€FÙ"Þ4!ÈÕ¸ ×2è²&šÃä?6JÔQGp-ûÔÐp±ÊŒyÁÞCгC½@1*ä‹•9Æ|„KM°KŽìîÖïb9ÀâëænÊsFjž* Ó,)L‚ë¬X³ÿu𦠭B!mOìp3‚عw,ñ“·æ‡i$„0ppT"‹eZÊ …îÖW ;{ŸàÙ/Î’Ö†T pÆëå>ÖoŒrj¥X€èP°ˆY>y_CÈ»ABÁg™÷`1pi@þ{|ñ:¾ ÈÒJ'- PøÑöøî=Hþ«çsÆÇ2ôTކÆFc›Í£é܃ /¬ c˦2&ã3ÇÐV¸7ãyŒgÓgûjEpºžN:ƧÕ@¸°pUY}NºUL_÷ièb¦ þ.ÏÐí³Ä±B»°ÚÐ㚇ÞUNG\béæJ‚'Î@è,Êãk´ý¥¥¥%u­¹sgÎø5‹YòlCæíCË7ÏVxó¼z'0“CÀ>zìØÂ¥»s÷n×¹ ONNM1Ööwñ<Éo'×N,»Ì°=MÖé„Éœ™÷Ǽ„1ætœ¸ ©‡øi1V!pìŠÏ³ßÎĶr tcbvÚD`Ó9(çnîŠ` Häf]Ó¥s@4•x ûX…[agaúâø S,¾Î⺃>•…¸Fn7˜)8ŒáQ¢C@ 8àn‚szåÙxÍôºÄµ,fÀsÐÆ¬QŒïÚõSX¡(E¼Тböèz:ƒuÔ£‡Ðì)¬ /?´š°= ÙËuP¼ 6¢•€ø1žc‹z¥tÀ¥-™½c²‡n‡<`=”i±Y-‡!öxžey)¬$nø[b¹À:E¾¤QÙ@ŠÇæDù¦sgœHKåQpÊ7ÛAåaQ¥€²(a*§àÑ9Ð<*y>_Á¿qùÒ%»–i²8[·]9`¾[®UÞ>gs p24ðô³Ï_¾padbjŠÛYÙKÖïO  M0£Fh ×T®߇¯ÀV3yކpï0¦:Ý+ÆJŽ[‘dÀé<€œQ 瀓vDÃÎMh×~K×ì\ > ²ˆÓJäÀ¤s¶™~ÇuR/²Ñ)\Ýü‚œ°ƒwÀùSbl ›q™_$¸ÅŸÎ2[z¦‘‘ v–Næ]àç.{_ìŒ5Êì Aã¥Å–½8×,Ûœ·ß½sg1Höš¯`ÎÜ¥iþ†u´kÑc×ÿ(ÖÞºÎÆi¾V)ôä£GÜT$Ü(¨âà-~¶£³\y¬@p©`†nô²ë!Gú}, þYÌòî/@Écó Šñ„ì'V!3jgùX)ÜÐñÒ ë¤Á_"-.øf[Kß„»I£bÐ#Î~:ÎÛ…Rá.ÿq`‹(-#”ßòÉ㣯‰OزXW…¸J JÁ0aã-·Ú2ЀÁÃE¹ ÉoãT4¬¿´ùÄ1Ø{ }­"æ­Û\³¿'¢}ðƒçøùždŸß°ÎÔ†å»4ÜĘq€¢(Aý¶ EÆ•¹¨`>û^¨|…B1þÙˆ)¿|W¤£â"?¼ãAʶƒ ·4ªÅý „ÝÜi¿‰åQk ãŽÿPzàÏ3F,°çâ:.~ìq´wkÑÙérŸÂ9”†Ø«€uašÓ¡Böu‡ˆ:Û6pO8Ð*w—í*éÝc'N 1»æ¥CP  N  Ž1‹ãˆu+hwƒÚ8ƒ­¦ò]˜Ê½öMÎÇå\x2†iUÙâ$bM~„ŒI\Ç oÈôr¶å«ë€ëF,5w1ÛÀDå°K€ç Š'~v’ñu9Èez+“o³ƒ³³ óiÎÆy¬óˆðæ?UþYŽÚ¯ÒEž”Ig¾¹þî@h~ºÐpg¹ÎY™ŠW˺†k]Úšîs˜3„ ¾.ÓOá‰g>Âexh^ø~XGæ&5׸UPÅÍܸðL*Q‰«?‹€DÀ;z¬\ÃG`:swº†à3,Í(ƒË8ë"Ïv:×öZSl\Fˆ‰ë3 ᦢdyMõÂ7ÞÔ úU … 8… ùúì$¼ýDc¹´Â(h]z%ý<ËŽ‚WšËž^ ³5fùÎöã² ÒhöVÀ(P‡ÈCîz‘4Sù°ß¨øl™åoX ó Ó9¾8Îî·Î {KMu=íÓÖE˜|GÜä…-*HâJ+òçK/~´Hç•û|ôåó¾/ºfgÄÙByŠÀ5.gàòÓ~íýÏ<¨¯øòL^@&œ§ ´»û!¥%úDéÊØÄVîC(Ë"Ür˜Ÿ}Ù<•°óÒ$ áR™W…@A3g 0—–}Âúš— ÂþtÆTP2|æåëƒ~()¦±Ì>¶ìΦå«û Â<e$^Ã<Ë®S¸gù_á/ÍP ij¼À„ŽÏ¤®C…/­.u÷“ȺkÆósÃ¡Ç ý\±e”¶Ç}7üb¡› ° wg×n„äô!P¸<‰þŒoÿPÐ{2‚$µkXüLý‹„îoP±¼fb‘p*Ežt.ÁÀq3fÙ÷@&ªñ™¦~„ƾéìßô¢‰;F¹¼µ1’™ç»åæLßïœß6·®¡€ˆL ã½C¡TÊ«Ä<[Ö˜xVYœhkàrÜt¦‡Ö V!n²^óUž¡.°yè ïZöÎÐ÷T^ó†FÚô2Gcg®OO«ë§~ž™jÝír 9®Ý.[¥Ûö À~üãÊ¥G¹ì0æ :åq:ó»è¬ïÂÌ{p¥ÛÙM¼òЛÜÊ·KnÐÙNÊ!F@éâE#˜¾Ð ‡ '] „ÀÍØYO¸x³Ë!ÖJnÙûÁ×ü¬ÙÒ—.Þ^ Téc ‚ë°|Ô7>gÚÙ1¤é*_¼r~Z@>[VM¾É* ›ã…n}ÅÓ¥ÁrÖÊJ5•k–¦?×Ü›fw‘¤'ÝX>¨ê)\šÂ½27,,ÿ¸ÎujÖGB`gTà“Nç_c´¡0Â',OÜ”¦¯%b !¨/FM|;†GñF†šÊ§ØÌ·ß¾8Ÿ/?ù¼oÌúÞc˜çÙ±¾q.(äÒ®Ë+cIß8™ýM!–.òf¹b}œrɇPà£8û8ž"8Mz‰6õÃCâ$mëfÞÒð¾gQK…z Z-*%–7}•ó‰¼ÀÉ™{ð«*—å®×ôÍxÐÃWA/>gù³žÒÖe¹y€oÙM'<ú±¸Ðʰíê͈ÓX0äIºã´¦—9³¶ŸÊG%8~·@ÅÀ>kÿkHï= îJ8BÜI` Y÷"xÎÏ›½±P^hIðû .¯(Ùt j˦°vÙÈ}Q®ªhã í*,>(õžûoÌØñÅx‘¤ëÒ†ŠË‚CÕ&œ²÷¶U-† «÷%E—Bôå^{>KËI3ç2¬%-ßy!ù¬2û~Íòé7©b8ù`KñG]ÇÓlOè_§ý.з¸³w†”çhë3´ïyö²\A)}套\YQ÷¶s”•"к›9 ï–Û– ÀsÏ??Æ™ò§4ž.ù§a˜õ=nÜq°­]ôËú)¾¨i¶ kíÖ!Ì«~+†›Ö¸ôM¯D•®VªgãkêC:h¤‘ôŒÚx-< ‡®Êà{—ëòÎ|›t–Éz•7¨mì<~<ÆÙº|É›/t¦5Þò8srm8œéuÖÄ/‘nþ+N”…€õMëDZL8~9[ÙH÷ ð ü’WZ=,=ÃæÂÂŽ$šr¬ýÖÉ·óø˜Šs1k*¿]ïѸK¬¥{”nÿ޽ųú¼¤G0Å~g÷és]ÚMi xé¦à)€Àf¶‹iQJë2»‰O,LV‚*~È¥ÛÒurqT2Žº¤mó«…-ù§" Ø ˆ¯p—g zËd+*dÄ:æ1/é%]­ÒÓ)ô#ñÒIam_Ȳ…¢Rá‰+žt³ü–#ñÍ3ò¢ÌúùË÷Êô¡è“¿åv?…åxÎ/Ó~¹`>*‘_…N|Ó¹¶Yó=8îÄwÏŠïÒe/‘·Ìå(°ðÂaé"Ìñ˜X‚ S>¸*ªÓÀSX'!óoº¨/ûc¦µ¯ú>•³| °nÍ2,…\úò9”|[?è“·<´>éR‰± åögË™Ÿ– Äv“êŠmD%HšŽâ—=bP8Ë”ã„^w9¸táXª‹ôUœ™§qNx\ E‰"¡,Óè7ð½€É›½ÚÙo@œ#ÝÞô¯þÅ………iúå3'Oæ^…òEèCä²/Ý*o àçþìŸíÌ\»¦¼xÿÊêÊ/ò¥µÓyŽó³ßÇ «Éœ&Ã|׳×$…³i4½I_aå ä /4a}i[¾XW'¬™³Þhdzñ˜úÖ§öISÏv¤©#>Ëë£õ…gõÀBâÇuI_âœÜÒe~é fN|óÒIKeDx8ò4/Ë oÙ9°è;“wC_S)°üYÆ’ÁóŽùS´jìØæ9œ¸8‡ƒ´ ua·e\`ãN}ÓîD¨°¯œ™cÙ+âˆü4õ‘#G îþŽ#¬½©Íœår€ 8œÊ(–« #ÐìW`‰·âlîWøÀB©tx¶ŒÒHáæ/ ÓX&yö8­p¥í:íÂ[VO…ÈRß—P yöÝ·wÛ7¢î„Ãå³>n?R*)¶¶m:HÛ:ÛÝq‹Ü*=l…±tŽrÎÒ¯¦éÞ£àï('Q:9uºjóê«(hU°HåKGàApòän¹ûVxÿ‡>ÔaPÝÏWÍ>>Åïãü¸¥ül0¤ Û}ñÀkaEçf®q*ŽF·«â‰SC%BÒ-»g K>ù¢:l'ž0ÃÂí…¹VoþfïÄ¥s@1¾6SV/ƒ4ÌK:ñãŒ_X¾„ñÂòœ.•Œx!+:†ÓRPó„†3Naë˜ø›_ب»ùB'ëiÚÄϼ›´r§} PwêÚøñÇænrÀÎTu¨&£rpÙK¼*½qµ æë#íHKœa;aÂÄ›‡Œ#*^þx}ÀùÂÛyʼnÆN_ÑÝL'ײ-Sæ/ Ïú5Z7¹ ç&xñ7½üÐv²|ÖÏÙÜ¥˜kô¾x1[Á·^ò%ßž(/ÏÉkq¥WÎg à„q‹P¬ç7_â¦!ŸT†PÒ¾BÛÍ~ÞYࠧоFYŒm)Ë¢€ö†<7{9 Çmù· ¼iðü²Þ,ÆOðú¥<Y³¹l ¸ânpð S9xó¿›Ì®¡ìgs Š…—kò'ï.© ¡kÚT¸¯¡˜¾z5„¦uîFö°S°¼GÀsýÒ¬‡Gô´ (Xµ \½|9öä¬[!辦‚Ò_* *.O8 J/LþI}iŸ”b¹ÊW® Ž<ÀI¥#fýÐ1Î<´dŸ4Š€3,+ªò Rù08Í2š·xâÇrB•>éÚÎ2þ2·î…gט!¯r» øÇ€¿Ê³õr=_Áß|W")loû¦ëÖ–G“¿üPq¸N~ •TgËŸŒæàëŠkÑ¿l{×­Ý­?U)Šd fÙUì3 4…•Ê‚±Wµœâ†9œG©ýÑxbjWR+£¤Iœ(Qâ “†Î÷±6~+åÁü¶‚›~«4õ˜PÅËߨÏŽK VË!]]ò2X‡SpWàð¶ÈŽŽaM õZÑh0ÏÍîæ|èü‹%иîy=…é…¯P騄‰ŸB« ¹S¼p&Ìp,þ¥½\Ñ@sÞ}“ñí«„¿Ž’ø"cÅ4 üÜç?ó©¼£.Úí.åxß(\Y{ìБ#ÿ#G«~ÜÆ°ÒÍŠËuŸ#.º­Ï7s¥™¦Fðå5=Zré7)”´E„p•Æpf³á…&~C^MBUúÌ#_˜f'Ï8“móì˙¶§IÝ,”Š*µåæ,&é…ÏŸ,»Ïjî¾8Þ¯ ~R(tœ¡{»^,Ud<~½ÆYÁÌÏ´±S_^Å3åsVí  &¯ãŠÓÚ¬™uÑ4™‰?Æé,Ÿu´LæB­ƒÄ Z*º´ÆhÚ^§ò”Šƒ8Y/˧" -ø¤é,Ór³Ø³„-ûe”÷Žíãrw™;C? QîfšGXš÷nŽ ¾ºÊQ?þy•ïã‹ËÅKƒlϽ ûÊ^ÝëÀ4†¢pñÆl{? ¤0^ea/˳ ‹Å2û Ä[^Z,v³Ñu{Zñanü›…Ö¾n ¡Ùƒ†3üP ¸¸ÈO _G ïâ³Ãnz!¬óh£W{ĬËìøj% Ç8:é»`?ðBkß<1 ?TŒTBv £ÏKÔë*BÿQ·'>ÎRo-)7(ßå$o¤˜AW?Ö4ýg¼q‰£Rœ ¹pÝf×LWblü›ñùžY÷¦‚°{‹'ðkG½a^<Ê7ë­’en`GœJå cJ̓۫`÷Xcºä<È ß¿ôëxòÈ$Òh‘§®Æ,¹B©ãÖóö»“Zú—ÁühŸæù£ ëóŸþt6ÛÆ îðS”ñÓLr÷…ðî÷¾÷ãûøï‡††Oرì¤Î.ò%Öt³áÄçÌ$;aêØ¦áÆ—KMƒNÕa³â5R (ñb™GÞ|QlŒ¤—‚°‰›Éì Z Ò:д$Ž~*Ùñ„ögúÍé2oýp”Ç™.6gá×q-ÿ8‹vVCdðEº±ñx^©šB;ë<ßÁVS£pë,svã¹[Z±¤b¡ ÔCOÝ××÷§áÍ£hëñìѵ!—æ€ñybÛÈÙú4³úãƒ\Ô³¼ˆíB—“ÀåχÅ?Ûé…ˆ50¹ø¸¼ö·¦ã½LÆÎô]æÑ Ñ%?÷„Ø'ì/ p•>/:I™ÞMFß#p,,Âùle¿ GZ“ì¤UÆRfâÌÛ>iðC1nÂÇ^aà[^¿²èûdK03,¿ ûNiY0¬²Vé…é|'|¿ò]­i nÒ÷Ñô¦Ów¼²Ÿ;~Ä;¯ó}Qù)UÉpþD©‰ƒ•‘Îô›ñ2,×V8Rr´tTäu¦‹[C³b<¯£Ë•y×E9D!Ž˜òcBS°[7ëèòW>àœ5ÇŒ¾…?–GJÙFͤ¾ïM8e@¥-^ã½ÿ\ôsZ៟|ùåK¤ÙÊàÑ$u[ál÷ÛJü‰î©€éqèƒùÈ¿Ï1ª_GÀ{Ï}tÒ¦ïàjÇ &ØØàØ¡„‡’À aXAœøE¥Å*à›\ÐkBÍ›çD5>Œ4çÏΧË¥|ZÿkºTšôÖ1Ê饛ù´ÊsŒˆ¦’ ^Æ[¾ˆtøƒF…k9òeÍãxÆç‹æËi-Š–_ €/^ª·¦×¬ê'kÍÇ%Täß(öò|…_ ÷ôµܰýp–Ù´18Ö•©k…“Kšmó’ë /Ì[E$gaÙ–iypVfØr¤Y—`¸hSB¦)Ïw—uv&âÌs7p­°8A¡^E˜¾›Ö¾@Ñ—YxlÉY¶çÅÐïÆŠ~fw7‚ë¿.‚} á{eá(ð>Âs’Úùû×ív@o?yx÷¾Î3ãZ´F ?|£Íw¹dæÿ<=îýnñý¡nñÔ"yü4ÉwƒÇu•¡l¸úþÂpt7ÈKýK ãËì;à[¯‘~?ÃÖ×èœOЃ”kFÓ*ey™<ßG^g¨Ò£€l®Oà×Ä/®tr°“ŽƒB³l1‚ï é®‹X¶…°àGïs(%gyפéLÐcEYN}͸qt¸åˆA*iT°¬ïaJïE-zÒ5½·ûY¦#0بƒvΰbfPáYÖÐy¶ gxGªÙ¯‚ÂÀSàÚ†§²1  ?h²vvGzÌL`Ëq”/ù xÁz“:iq„ní%5.‹ØgŸï2Ï :¿›ðQо9Èl¼IÌó‹lë?”%Ó@IDAT`Þ ó¼G §Ðôî‚”ë0x+àC(ŸöËs7ælævN0N]ø Dl<´­·)ʱuCI¥¼'{+Å“*À®Â³—øC¶×_%í °=ðt–ž{<Ëuòù@ys“ü8VŽK„w’¯›2-ƒŠÉ£¿Ÿ”GÀ_•Ô;Û]\¾°P\ ­ñ§«0:ã׊ä»ò*iÅ×|¯ U +ØC ˜Ÿ}R!~ˆßy~:—Œb&ŸKN¾[* ¦}gð£ÀBQåÙw"ßÁ°zUc Å(ß)ãÁsÓ¤ýÜ%+­#M¡ j|fØ)¡7öy„OËV|t‹ô¾™o„¥m¢ÊE^ù°É·ïYß\Æ0:®F®(4…4›´2œ¾iËœKHs–Þ\;o–­LS%«@"Íôâ;5Ó5Ãu²ŠQÉ›a=Ь—ÐÍÏ·‚ò‰>ÇCÖ=Çʈ÷O³Mx‰_ã%î€ Ê1—¥³i~¿Ë­ˆÿ ²èë|Á1‡ŸÍ)ÞôóVcù›Nüˆ÷Dàƒ9Ï:|øï²áé9wøwXl ]VÖ} ó0²³*"4•ÖpÏt ì&— ¤š~Ð7ðÊðz·Œ¤tÅ©ÊT"i#7òxõÇt7- KY¯$™‡ñ §0kö–œµ{¦77 6Ðo 6gõFfç•NÎîSˆ6ãV¶@r¹ò«ŽqÆÏ4ék‚Kû—fT*¤óÀtnðrðµ.ºš×À‚wÀš§Ìž{vj>yÝÁÖ6ÒÚ`ž 9ù˜Be86ˆwàõ"#éÛ¶rÎ( Áð,Wîˆþij¦gMÕœ Ó¥ø¦uÆ©pÿÁc‘B “øi6ú}e®0W¨èø!ŸüD®ëð‡yžEÁ}”4nJŒ+ÚÎô´<±ÄGu seaˆ}ì-8ͬÿ{¶ýY¾BïLÌ fœ1¿›®k{s¿¼þØ+cYÜ%7l©0pM[qú×ñ-×,,ðüÇÊBMTŠ(ó^èͳLq ¥à]ðóË´ê$¼žGY˜ î5xþÿ:ô¯ ñÖ°4(P¿Iüq¯’Ö£Þß Õa'þ«ø o­@ uïUüƒü+0ñϯ Xôã²èÂS(µ³˜Ñ_%ÍJd>~„¢Ë…5l\£tï¡1ÿ)£ïA"GøÖêrñ,µ¥¯v°(ìÁ4Óûã,+XK¯¦U)8Üûü¯"äÇøPŽÊ’èf‰CØŽ‚ççyGI÷â³ÑplqÎyÊ}=ã*Ôã,kôs¤S)ñÅÏ ¸?¶R®¡“ šÑ@ñˆÜIOÁü‹ÔÑ>¡RäìTåâ,yšFEKþËócÀ=²©Â£3‡?†¯Õ* %â”ôyfŠ Tö3@ñžÄŒž°B]ÚÙçSðC.ú€ñ:qìoÒsI+,e ¾nÞ…‡MmfžQOᔡ*Å·‘7ÿcÌï¼ðÅ/^|«¥÷w˽ã Àðƒ¿¶ïÀÿÜcP)ønùÁ•d^œ!f»po‰î#SL_æjVÕ—¸áôÌp´4Š…NÜpÒ$¬2ФoœÏñ‹ælv·›ûÕæ´%ñ AS‡¢ÊWü-aÆgiÄ g-ë-\ÁÙmúä§BÍQç âìÒ—_'Nš0ÜræbœÎ,•y·‹²8šÎ—X¥JåÀür†b=š%Nžo Ä‹MVø:ÃÒ‰=U^š~ø“oÒ°%ÍËr ·/ñÙÆ˜¹)œ‰¸D>¾ÎÁÞ hIGÅGº $Mû¯RbÄÅ¥>À9íÃwþL/a!/y°!oÝ¥iÞZÞGŸ;ÃŒ÷$•8 Ma×ø=î4V‹èÅãpõeZä(´çé«|(¸8Àl¹GZ7Ã9ƒ\¢,®åŸ×º†‡´=„ú$'³ÁOkË,Š‚‚Öú 4#Šú¬Õϰ_À ~±n3½ƒ·J÷!p/LîÆQq@è…ü»‰þ.åØRíó“ìAÐJàGxÜ#ñyƒŸp½?osc\½<z÷ZœcN…B†…’P1ÃçøGË6ïTj~™†_¸À'T=ohPÓR†´$€IT6¶:P¬þ&ý0Êã3t 벌!ØRþ±s:X:˜”óÁ̽ŒßðWZÐLWkÉ xjÿ‰Ó´8ÙkH›xv¦Ÿ;¬ó›Šq’€<Êš””å·% qÍ|JŒ2Nœ<.•eO¡xé+8Bi¶ëÒÎQ[ú†íã.~ó9ЛѼ‹\éðåñAâUÇ£ªÀloÓXç<fŸW¡âYsgÙšÁS•5#g­éŸC˜ºî€ÆrPÖÉ-®—_°cô2ÿ}Lûa:êzñAp4©kŽÝAú}ôyÛ\x~zÖã‰îrßÍ»±‹~ç÷ ,,óÌÔ_#PRòóËl(.’ùÌóšüµÀt¹Àè2· îE {OÄ3~—'žêu‹¯b¸À{wŽ2¥Ô.ø¡…—V…)¤&ú× y! ÌTÞBÑ×öûùíÁ×jÀð`X rÖ¯/_µ8xAW~É™)ð\¿7ÊT¶ç8!ÛY¥Î>•íÂcÀŽƒxŽpì>‡†V• FâB±–Îg•à*ëºOŠ‘y&®~öû„婚ì'딣é—{¢\€±+àm×øðáLŸkïQ†faä{¾ÿ)ð3­–Q'?éê÷ŒzçÒcÄÁ—pÀ7ÔKxƒG5®°Fš'^#M”«ñ\§/s»åߨë-cËö§®Ù¦òØtɇxàO@›Ä7‡+Ä&XÐæç@˶¨ÒlÎ̾Xâ•~ü­`W•‘²² ¨ó©/ÿñ:I½‘_Ó~#ÄÛˆG€gž{î?~ä±Çþ Ëé ?…¨—Ÿè¢!kÅ€öú‰ã…#*ÉØ˜±©àÒ´¯ð'ÍôkFB7…}J±„n¦¡zÎÆL?22:âG v:B ue?˜"ïFÝ"Âg]Þ×å-±Öÿ‚“䃇<˦†î˯Y¶ùÒ8¨T( ×q3v2?J‡¸0,¾7û­‡˜™Ÿ´Õ‰S *¼åÍ%„ý Ë 8ep’¼Ý4è1ÁÓÀ寧¼@ÊÝö*> ÿwå&Á g™‘«|ÈC7+z"âY)h¿(y¸ÙÙG óÙ.âïß4ç.ÿ’Žx* # äg¨Ý'àÌÔ¥/Ó5Û›ÇÚe¿²º wãë«è¨J·aû\sIÍØæw?²œâ¦KX\Ï›Q0ûUY~q­[óý%x³â¦K ~w)»í¥óŠ>,vÏó\O(ÄWÓ®`âj)Š}: zâF]“#U>‘¼à³H·pe©ª6§Ì½Ìsã²\‘Ž?Q”͈ùÜ@n#¶¤•ÐL¾ð›]B³Œ7cYÍeL¾¦qD¤´: ŒÛÿÆ—?ÿùÏoð'ß‹7@»­èwLà‚“Ï>÷Ü |iíˆÂ\S«Â˾ÎJúÅ5…½)g^y“˜B>âR!ËéÜ+  ¡Îs( à«dH×›ËÒI[§RŒ5iÈÛ†ªPÀ‰f‹FM\6g9µbAã¥ì¥*vßìDLqÖ3TìlÂt†Æ) ­¼K*‰kùà'¾®™Æ°B?}`ЙU  ¥/,„>at2OÃ*Éø´XÞ Uå+¾.ñ2>žÅå×tù\B/c…g=2œ¸uúD®|É…¼æS…¹%ÛL#u}pT½RÛwÐñ²‹äzY=UyK7óIá»yMÒ¥g`1À•Ô“NÌŠ-¥Ù¶J—ë»9 ¯%ç Ó]×vYÂò ?ë³á&OÞÞAüñ~Î5âVšœUœ†©Á®ðñ6{éÉ»˜E –ˆ³_ìD˜:ðk¶Wøxîøš†½”ÈÝé*t®„§0÷ÿ)ºœ»ÜŸÁﺺfø˜Í‘vH!oÉÜ7ððžoÄMⱸ½z?»„Iþô/2ã~|t<>²ÓCð.±†ïYqî¦K$Γ^áÊ'ÕÂ÷èß{¨Ý2é_¡’ßCxú.aœ ¿ }ûÕ ÇýÖx/Có=”åE6ZÊÐÖR ò"T Üàç@y­åó”V!~€xw›»vï»ì;ö°Y`*WÑ÷yn:¢bÖ¯pwï€o«åQI¸@>aÚ',Þf—q” Ðäãw]²P +Ôµ XÞ|ž@»Š<ùGTàMñRÍšy•™Ár€6áõiF‰b:Q£ïTa¼ НÏöé…(UP¸x'óaSœ`•lùeHÔÍï\–Cß²X&ßÏõœÊ°ðÚe¢ðEðys\'ƒ‘ x‰ª¿Ñ5cÌy£cš ±òg ^¬§Þ˜K“Ÿ›b6aÑðxãYòÒêB˜gù—ÈÓY¾BCÓ·û6 v-*šûݰx¸·~…ßQà§øÙây*a 'ÁfmKSt©´ù¬3¹¶®²íeÎgË’}Ô6U9Èž½“ò¸éPç_²­}…¹-íÑK-#úM>QëÈ%‰ÖÖÙÞ!(ú5~ºæ;c^:óh*lü1>-mÒT™¶? —†ï‚tå…)]ÒŽþ˜NXUÌÛNòe3nDæŸHÄû]&NB¯çgú†¿Ž¾jDß+\U§ÛÞ:6‹”ð*Õ-½ ¹7iß2ÅÍIØ,§ei†3UŒßù€_?S—²üø"x>gÊŠ\`%­ós_ýÒ—>[ü©éo÷vAïˆð#ÿñ¿ÈÀßì0hÅn*® PŒÏ’R { ogæVÚ—"«¾8¼HZqÆâ¤•@Æ$ã’‰Ö*Ât'}˜õ¥mžÄ™G½QÐxò3]*'6c,A(œq¹ù&;’u“–δuG‚–i³~Êxp…EyLxë)ÓK/p lv¤1GLæÑÄw‹ü«´9 y¹‰ƒ–ƒƒˆ89 Üà&Ib¬äfp¢ª‡ ²þ†î* ÍYt–Õ¹¹ëY|glÖŸæÜ0Ïã[®tÆ ÏÁ9…BÆë—œXOg™´&XÓºv¿°BÅMlÎò¼K@Á/o—a0^á£2âLz§ïÙvûv¹SnžÓjð-3‡öóà/>Jÿ9I_x¥º„ÿüSá âÝ 7DXáM„0 ´=;¯9y7–wåÏ’——ù¸ì`¾»¸`¸3pÏìñŽì掗röË®$fêí:ô9ö#¸¿À3÷*cé/ÒפtÄâEn \Á×ô­pT˜j݈ëz­ÎË’< áÒ / åÅ/÷=ø±&gßÞLøMÓ¶o˜ŸIF3¾<µíNy™4[9ëiL)×q eßÍtö#×éµ\9Ó,ú—ÈUòÜLi¼h–K¡›ýš`¢F|•¬Æ•”y7}ÆÛŸj«áT¤›}4Ó$Œ¨ú3®"ê»è;™U©3«%ú†¿Y˜ÄÉ ›HÀhÂp[E7rl¦Šp¼ÓÑÞ<&‘›°J@d!Žø·ÀÍbÞ*~3éšææˆ»ü\¡UÝå[¸ªfØzˆÿDJ|ýÄmúeü90äk_þò+AgÓŸ:ïMð;ñøN(ÿä'_}2„(ƒÈ/˜)t½+p]ß7^¸Š€8ùS°/\f(äsöÌQ±È°~ùjÖŸ_334ÍGš†kG?S*ËcÛ©@¤às4 xÂTÄ èeÝôÍ?ÊN~ú4ò1-ñ%© ït„¶\î%èbŠ4O/K‚eºj¸qV—åeƒ¼ÒùLdÙùÒÏÈÊO^%Xêe‰Â3y”ƒŽ~)§ö)_–ÔØyüʵÀ,GåÌ;ÓDXx#>ñTty$°|Úøw+Å gbfûd*eR©hâï€ëÆ>פ-­û œÅÉa2Æ,–g­ÎÜ/‘à Ï3áž 'ÓègšF˜$¿Ç—=ÿµï|ó›ê¢\âoÞ¡ûí]u|eŒÓ;ýý \™•‰Ù~Cèk²W¸k à Åòr JX‹¯0W0:˜ëTro8`¤ 3?O~¦u#”__³#IÃt¦1¯˜é<ò$>¬Ä›—aGëP yòì×ÏT"ožÝWÊ@§J#MÇ@aÒ[ÿAþ5W-ÆK¿C¾‰o¾vËe¶ ÚÂÄA} :9L×aðO'Ii†«høJA" ¯ÂY6“¥撳؀[&`9¸åìE ž³iópV¨ðgÙ8øžKÒÍKT gÞ_>‹¼Çà \“·&]g ãc@ œuÌW˪h¹q/](W ü›Þ.iò³å,-ÒB£4-WƤ ¥°ƒ½Âü ÂÖ½‡h#­g*Íã—H¢`s¶«¢ðQjö"í6I?xús¬£?Æy¿yþØ¥¯YßMGè^$ïC_k€}Ä{,Å<3þX Ü™¿B_ô˜Õ¹þ˜sžß>»¸wŒÚ8) YÝ/ïyÂAÓöæ®[ÞóÞ÷>ýèñã_£S s˵~™™;úe¤³iŸƒ–‚T¦†F°*t‹ë7à ãS™ˆ¸ª–#Ëž´Œ_ásª~ÅM˜JµSXg9"žsÙÃÁWž¥kâe} ÑMš›ñó¹™qÒ úò',[ÂB§'ΖÎÌÅ«üúîòÜRM|ó(9Q–>ro¤w¹Áß_¦iú‘qÎð5E«KS­3{MÎé|=–çÅ5M—V­ "óØ Ø1.Ma–+0ï§«pÛÉMrßApßýhmØOÒ ’iMðL¼\?_¦ÖçIúÈ>1å~ž»ð½WÀrxêÁ[ÝÁ¿NEb†4"ÝN£H¸É..æ¾7.ÑÀõüþNÏË„ç¡éæ¹£ÐQá=#®ÿðçÈ_ìRÆ)à 3?§{™ÀI ,/ŸâYkʼn €ß |4WÉÿxû)ìt x—´hUq)À6HáœJ—Â_å)gä¹>¯2£[´±ªpê¹f¾•r×@`( 䥒 nEÐÍ <%ÞRÉ8Áþ*Rµ(\ƥ߄—)²Žo(igßÞˆEÊè7Õx`ƒÜÂÕýÕN¶^Mçq[’Ýwžñ懫ó߀p{AºYæ7E¥*GÔ=Ã[$Ìñï¦(Ò”5©bò¹¢•qëéKüˆ®päEÆëg=&e&°ßºzùòŸ<îœÛ^j×Ä©w(PNŸï±­ÈLìØñ^…¿ ¦0×OE@¦ø¬0ÏUˆg…ùÂRÌ\]P +¨—øz,TBÒÙ½tT ÆZüNºŠ€ ‚ž‚¯ Úà«ÄÉJïøF»JÎAÜò”Ì>‰×)ÌÚÒ’Ž?Ë”³kë Kß¡ Q/Ê•‰+;€%`}6ÓK¯Ç̬ό^Ýn¢¾/>qZL“ ‚õ•_D-]Ìx)“‚N:ÒSyÈËŠŒ‰Võ |ÃÒʺKƒ°‚Y\qê8aÄe:…†N¼ÒÆ€öƯ×þ+ÚÆÙùR ›‡ÏÖ/g€‘ÏÒ4^:â›V˜.}ÃâûæSÐ6Ó³h#FìEpS¢gÚ¥ë"!ðóͳ yò`>ç‘KgóÎøÓŬš>á‘4- ‹„@ø83û/ =ŽÑ#´¡û ‚Ož³=¡i‘4½»~ï÷´±Ç“ߤx§¿÷×?¢÷éËã«ýÕâ‡8ó?ËZ¾B Õ™£Oü5K…çôêÞpx*nB±ñð½'ÁSšò5`iaÙËÏüU¸DÓ¼ïæ<Úô kùU\ )…ÀS"gD•.” Â.3¸—"•óÒÙ+Ô ¬•>WUˆ‹Ó|X±LÙLoظ„YÌpf Ïšï“ðÀ«âb ªpj:Æ5œ8ù Ž4ø Þ žùo¤Z¦ºušõú$ý-ýMeݧl²£ägÙx¨ùCT–/ßó©ÁŠŸÁï1Ñ òu¬4“ç `Ÿøô®Ó¨Bë€:y¯¦e#Yò4{Ú~‰àßÞè.>Üu ÀÓ?üì_9üÈ£|î—*Ã-fÿ0Ðo¢Û(1{bR+`sÖl½ÅÆÀW¸eZaòX<ÄOÁ¯3mFü´Â…™F\;}6pΨ]«—–åYB¡,©°3e‰Y;´¤í†ÀTŒóÇ:NÄe9è݈•B>ò <“®?œñ–%ñ¢~‡Ÿ–q]ÿÏü’wI#üF:©g]7Ä5º¬pdËN_¥`3þ‹¼õ}LøfŸxãn‰#mêÐ,Ÿ$6§qwàt†ç,Ñ™¨3b— ÔÒ×e9Ü ï™qgž~ /–"£ÄIa£àW¡p†.ýæ ž´‡óÄ€Çõ,oœ)§ý'i­ Φ=/O4õ+è½^8nHD€k•ðã9Þ®çí|—)ð“<û‘"7ùYÎ/"|w~7ñ§)ÌóH¿E Á{³@ø»ÿñ§ÑQPÍë®­»Üqƒ’ h\’¾Ç/’W¹Ï „Í#n/ÄW`jÒÿ>ðÇ¡©BãG…bxÍð b×íµ„òDØv¿†g{¨èÄQ!ð1•'ãÍCó½Š•í¦’3'“«t ~­ò\°mš‚\ÅF«À‚:¡ Y†t?…{F‰ftÓϸ¦Ÿ8MØÆð-2)bà½<±OÜÊÕx auñ7¥ÝgšMñ%èùm+¾Î¾y“´Tì@Ý:î&*7ÌËw:Ê®ÿ:®Œ}œ$%-P#¯×É#Úhs<åÚ@ª*çM°¬K3}¦Ý¦L+"e˜¿â7q›Ï\òâ Ç^>wúô_Ÿž® –™¶Y„;ö]½«n×®ÝOd á˜Ýa]AX3»GªÆ¬Û¹‚3ÖÓ™Á”ø\Z̰Ò½BØÙ¶ŒQúì/„18qÉiŒ_¡ïº½ ´€ëë†ðñ÷°,è K[AmžZ'ôë=à$­‰Éɰ6H×k8þlv áî³´¬‹ßíÎ:„À®^ƨåÒYNó´>Y/_ ôÖ/à”}€³ä†U ä§/¬ EeðÒŠÒ?Kºb’δîZOÚÙQ±Z‡C&€“ïo Lœ@¨^ŠTªdeZiGÏpäG?`UzqtI'|6sœœI¡z'vn¨Š}<ëT ÊÖ‹ÇrÓ AgåQW}~¦÷—¸M“±ùè\*pfª3,mÍÿš²Cù¨Ê¦ùÜôÞØcV®S3gÑ <³S:>»©Oai¥|ö ÿ#„Ÿ!ì³÷x¤îhèkæw³ßÓôK(‹À‡¹®×:¹ÿ" l:…µß"PÇžf†Æ9Óöwú*ÖÁ#„ÎÐÚòÓtŸ‡®íý tÿˆ´e±œ³Î åeÒœ@!ùšŠgãT|Žñ“—"Z2¼}PAM°¸Qá:ë&y)üKß¼-O4†¾Mý s϶«˜©x>ßn"‡ñ‚¶õ’.^ÀÜiܺ«ú ÐF/$:S˜® 'Íé+JUÿ,ŸÊ<"•pÛZ¿Êû&øzaʲm U–;ú}ôñ5¼“Üæ÷¤ÎÂbÔë͵ÏéX^ÝVé¢,UýŒܬo¤XÕßç ëW®.k#¯ÀK„Ä ½¯¬K3ùFìÍ4”éÊÚð´ÝÄ1Ë©+s,Ÿž°£ü[ÏtS°ççf­¬t=©SÐk-P!qVÝ,“³{/Sš'û¼; ¼òKT4akЬîŒ8îàÙ£t^¢£é]¡ìI–üä°α ÓĹÉβå¼Ä;äæCKæ×§è—îöw™aŒ4gÄ»ˆ÷$‚Ž^¦{7é¦Ð—¦Õrúì¥u«‰÷S./:Fø*¿¸ø†x÷0Xw¢b½>ìmÐS±l äp<Çþ žU äQ‘u/Ñ J7Tèol=ŸMÂ>ë„¥3n3Îúó†”Ôk³»F…WÇm‘N:¿).Þ5ÚDƒÛü܈lân…Vö‚ ¨òy$ D6åž1uÝpK<â=µ@ä•<Ét«Öu˘F—é Öq!`#dýi=$ê&´L |#ÞDãÈ{+œM©‚^w«tUA6¦ud¬Wéë²TeLúú5^&­pjRa'òv#3yè™u&=òð‘Ic Ì€Æy>þ8° Iä‹#LžÑcÄ=AfC#x”¼0„C zšr¿K¾Ám2È ºCg¾-N8å~NêOúš|qýeRûÛè’Ël SxÑÃOƇÁMÎ `´sF ¸ñ…æýz–·qœ1ÀгÌÍàÙ=yæhøÝÓÙ5KÚ 0”¥“–ò5Zª}n>‘+ã.â\®ƒáü¾ÒYæþ¦`ÿ^†“ÿzÌÚqøÈbà €ôkx`ÔDÇõ¹ä‹ƒŒÜ  -¤]ª×:¤ Õ`hT>iSN M¿Ž©ô¥»*=írô¡5‡%âÊSo—M´Kw4|%^¢a·ö3ãÛpz>IéFdx|™K^âƒOxá.CÂ÷žÒ;lÀ†vN[À»è/úÎ¥Ñt,ò¬wèÀØ,]i+nb&ËNèx¿0f°!Òƒ¡±øKX‹›Vá´óYLViÐìïÿÛÿð£ý×îRáþZÏìs2´ïËøìbÊ0†18ÌJ/t:£Æ‡m4x2F<¾³_…áƒâq¬%z FÚü„ç{šõc䘵cØqð}ø™† ᤑ‰ñ¶B†/|XA@' 1¸ñì¶Û™‘»2¡uþt«Ž.6èÒ“AGÇAߖޤÁ™èŒA²ÀKÃaõ e_çUé .X-€8 [ñU8ƒøáwM;Î0Ó;ª/ÁéQV=¢)¦iøá¼w}&O|ýCÄ5VÎGÁâÁ‘¶tÞ pJKG6¸,½Ç‘ÌÃ&Ác{ºÒjÙšÃuU¦–7ð7ÆŸ0wï;Y̹LȆO$ž~¤aü `¸YÀÐcXùJŒ8ÝÎ;õ¬(à£#ÆœÓú0dpÀ,Ù‡ûfFŽÆ+è¥uîÝg_ùœžçâ öĹUΛœ(Ž^l?0(á‰ø¦ä,×3˜A.7>’NèN™ ƒƒ©æ²äê_¥ðTã‹_±*1ª¨dvŒó'Š÷ún8~fGj£ê8½®Â¯ ¯ói0<¯>á7ØZœ$«"+PéºN³L^ˆ :ôN2ògêØ­ü§”ß«¸ÐÉÏ3êIÀ‚~&¹•¹ÙC?à‚gÉ –òˆ6ñ7àŒ<¢vëÉDt=ƒw A€ä%i‘›8~‡zaA_DÃß 5_Þ’×*%¸…qžáC&gñ¡n.ñm²e¾§à8V_‡ú+ÃÞè@³ä¦Œ”%n…ACa °©½LxÈ!î( udÞ{—ñâ„>0ñfà™­ƒ‹fFõë‚m?é°ÎÃÁÔþŠ '†–´¤7LüÐ Þ\8Œ7 .*B&´è.ôÀ¸(Û À§ábÈÑ|3À€ùb`zš¬&ìݼY:.ÇCŒ.Þ~œ(ÍeEy釞ÈDtÂqØÒƒÁIËÃJ£D>4\8]§Ñcƒ:j«6àz`Ñx +<\ÔiðáW¯ #&½íð‹Ìy7}ãSúÛâ,'F_è­_ã~Àp2ã*ÝNpŒ?àÀÀ’a5íŽ§Ž•ßh( ©nº \ã¸1Ú¾„·DH»[ò^׫#…ßÒV¸ò_þPe-±`úKù.軈ÞÈ*)ðÔÍŠíŒW—†¡K|/[Å‹2|?ëgk’_èÔèÈD7в2ž™£Ab3qÒbÌ1t„ÁåÇ¡?ÞÇ—>,ùbœ1ŠÐÇtË.$ƒN¥á‰±‡&¼™Ádz¼Ž®Ðo–ÿq1šÄ¡ cÜg <ú [8 n¶•Á:C zäàRÆ)çM¸€pƒbáÕ™…Ðò¸g%^œqp>DÞÄáÇì<˜hrÍT8W $ýJð‚b>ÐÖÒ!0\d%¬RÊ·Lx²õÁ ƒÉl•0¼m<ÃmrF‚Ã!7py• ¹¡ŽkY‰Iœ2À€³ÌÿHa†r¬ ó[ —wã›–fúSÁPÜüáaÌÙèá°áÇúÁKw¨Q 8ºä0V0˜Åã>zóÃÁ/aÎPGöZ‚äypÅà;Ú|Ç H¨pœUB…2+ïpä÷HÃirˆ Á†µ_í.nÖFâvºòÛñFË´ Ì<)À†:–­è²†2¼äF¼jyÅ.õRXkþ ¡ï¯·dêýò<Œ’À7Koi3 ÁèzzÒà7ÒŽáÖªrKŸÉèhvMÊŒ—‚Ò8¯`£ž]GcWlƒÎÿ‹r.!(©õYÉ_!7yC˨2Ú4f¹È|3æm9ËrR¿|óÖíÛßÞöì«;Ðu£6N<¤Ê¸g‡-Çl\kxØËÎv  ƒíýùfˆéÔ=“UœãA†ÃÉL0¸Kâ§¡•lø€ƒNcŒsšpØÐ* >ÈeEáP†½™ñgvl~áÆ—|`¸áIº—ñH.ئÂÉ«õ.²H‡7[ bÌL¾DÈ€ã‡î¦eåÂðÆÈKæ™Ò3èpù‰GŒÊMü2¨µ‚¬…å ò‚OøBËÉö ëŸW ÔÚmtU¦ä_´tLèÄÃÃtâæ§Ël%¤Ó‡¦Zjå/2˶A–O+eß%u®xÂHÅÏ’;ç8¡>ƒÒ¡g™~4ÞŠ:Îa?eÈgÀç<ª,Iîz·p#q®Rz<ÿ-\G6(åÓð ì"’ˆIƒõPî_§TZC&#¼ÁÆ´’8à7¨ÅoŵÓü;þ /<íèµe<üÜz(Ú/¸MpFžnû-/#œpžèŒl‰4Ä­Z“Z';aÅo#’ô1Ü`ðYx®Í@ÒuoÈpkõª¤Kisî&nù'…:Ze³Xô記!UjÊ}]ºQÚ³?Òouj-­Ã¤;òÒI²Ó,ã…U¨C8AÚƒlÌšø›òßè@û凧wõ9e†‡%Happ # ,³bÏ÷¾±üÑ8¥Quc#cÈ!AŒ¼ SÙ>8(ZÏbá-#‰Á:ÔÞ=ËðÌÊáeƒ Ñ¡×íp €ÅèzPÑY–ü)š(:äÐ+¬ °”Ï i ¸4ˆÕâðC×k’‰1fð‚Qׇ“œg Þƒ•¯?‚OYácðÑü‘à *È£ ·hàMþÀ†¸È‡ÃSæÄ G7Ê,4àmˆ8ÐÃ2HÝ1ÓwY‹2¨ãêv<â8¯öh/¶uH7.p蕈^)‚–2¦0æ‚Ãi88%»,ƒn¿¼€ÐœCŠÃ«Iy¹ŸÕ yµ0¯âQ¬ xMIqfâqKîÑ‚fúàãÐ5ÂGpðÁAƒ áÈ ÌàE'êã*®Ë¯ñ6Þn„ᙀWh”þ*%¸áÕSD°6l^JY1¼œ‡&ÐôIyíïÚQ1ŒœÆÂ*^ú3ðrÃ!ÞêÜÖ”ŠŒºií3|Ì;<äS¾cÞ‚g¸"£n'~àèŽföv-ਞ\è.¥ 7däm]¸ÓöôŽIw/$v:%ŒaðñЃ®þâS®Ž'¼ÌrLt•ýÂæ‡®Ë€d‘6rA_×sFÿð™á‰¼1ð"¾ŒZΘ¯A}ì©(wUööº‚ot –D§ŸÂCi - 0Œ5÷ÛSxäã‰A¢,ÏZ'Og W¾èì\‰zXöƒ$kR†P2üÐA« 3 Àˆa¤0Æe š+A|‘CZ*ZdaL1è„)0xa¸øAƒløBÇjÆ“å| 3²qȳN 30°^ [ž|Œø_Vb˜¹ÅÐi‚c ‘áÕé‰LÊ“tëNy*Œžø «gëÒ|\ϧÂ6üÐkÂÀüÛwîÔ€H²„À¹8øÇ1°Ù]Ò¬‹ðRÀÇÁqÿࣲå±÷ŠÃ½`Å8r¡w·8ô\ˆQ!ê/0#Ãú6¬ùŽ­þ´¡7p8%Í2T¯öŽ"+6=dZÉd?ôuß@¡ÆlUölY-úè¿(çS6õ-Æ ìü‡ãŠ.üzŠ=\Ä=þ3šAÏÈš¥lºÞñZšå5¡ÉuÁòÑÃÌyµ°=þ¬sÑ©¥Uû(%ø›úv¹©ž—Îûê6^¦Áom0zZNèþ˜¶>ÌÓ ×þD^Çm²Œ’°üux‘¿Ð°Ó!.`¸à·ˆ½Kð5x+nNìz­£%“s|°VõBZÞVBaÏtsBû“òHT~kr%GéÔô3¹Â³¬Ð x´ëÈ[Ò4t{N[ÈOz§»"ý^ò‘-;òmY d®ðFÜ(>å‚T†06TȰ±•F'É £ƒc%|>]š2‚a\Áô,Y>F߸â ´ÙnÒ=Ó—ÑÌáCV>®·Ùènß½£í‹z#Áç ¶9¤¨oƉ'tݾÎ"²¶4kËÑI:²’@^È<ÑÃlÔ…8 ª2`àÜ´‚³ßìŒøcKÁ†_ü‘ =p|¶rüƆpiüä¾”+>z€ éÈ¡ qèæzÓÀOdvÐPsÖ¯áIžÌþ¤·´ð Í=ˆÒh+âh… ^u¬¤îÛ… êxó-ý›ÜNÔÁ5=|ô³¡_ ‚gÊÿ•úuNpR’ šóÝq“ f¶»~¤„㇮2á†ÓqͲ°k(v™6ÄóñpÊ0£ ²v‡@×iãݾ<0–ÔcÜ<—,9àÚQ÷ÒÁqÒå‹ðw¼á¸½K!YF@IDAT8¾aE¹RCtWñ©$›YÇñgáè€ a·hÅÆˆ“;gí¼‹Ò$ýÏLÿð±LŠ øÜ«°„)ø¸YºC^±ø3È5zK¶ü–¶ä½—pÓ[™Ò&xc…¦óO{\¨µäåä…®K·«è¼ÀUÎ墤ÿj¶w¶Ô)Ÿ«gG˜¨ .†ÚýéI…Áis†eŽ£Œ8tÆÌT4.Œt ´û¢±á‘Ñbé <†X2ƒ‡º¸!ÃGiðÎÒÆ¤Åàzé]0xðcP¾à¸£Oôô¬W†c‰®¤Á›YuÎ@Œë!«£|v<óg@#C¯AÁ™ükú¶Â‰V¶v¶§ë[õz!‡ðpXH.AM76wŸä•²qÙKVDÈ:£g+У/yâGÃ=e#8ô?ê*« À|~z®ó/:óU¾lwâ7Þ.·V^¤¹lÄ#ŽòÃÁ^ðFwœÃ¤ æÜS® ݜ϶u.N(Ý­ÎtÐ*Dù¡5qËÛÀÆ4½·<"W¾ñ[ܸÀ Þ\Ç ¬¥¹<޽5ð°iÒÚ ÛìJVøÙ!~䆑çºôpG åA[hå×yT´ç#Ñ¥|óh ©ã%žã’•ÛɇË9È”¹œÏr áŽÓ`…Ôpƒß Z[ ìK|“¨u64çg ,uð‹V…£M¿bT ÔÂ-2Ð)h#¿.ÛHúÓè‚{)½z Kg…¨ÛŽ¿b¡PÁ{úR'c¬ù3à|;æÞa-°Ÿ´_†æ%¸£Ñ0׋æg*°*22£á‚hŽÏ4ƒŽQ#‡1Ç(äa§Ãö‚"=FÃ|zÆn,³ûšÕRˆgŒK™{%J>Ä Cé8ÏÊÅŸSÕÈ9 ŠÃ×K~À1„Œ 3þ̲1´ð†~Oy`5€Y/²8W`Øàt^\2ä™´>w s¦Uåô|ÿÀƒŒÆ‰V¼â¡|bü¯mj "þTè«nqõŠžÊ† }Ä#8.—–wï]Khqho(΃ß6>”ËCåˆóª‡ð)ƒkúᨳCå >äƒ4~Ô:åÏà‚…ó­²¡|Ð)¸”q~¼qC @ åãØ4pÙ·8a&MïH <ˆK!×5ÔŽÃO¿ðw8³”e5©Z*ŒD£qû ¤Óî€áŒ¯xèlŒoÄÂp:tJ³q^Oj\;îH7„/¥7£N'i­l€; ß ™qÏ N›ô+`Wáš ¹ÔÉ.õB²ëJ~×g€õô¦opƒo Ôs!ò·óaèÚÀ»ßôìéWÅÓ DmðGÞM®OdÌôjüg°Æ{Hšñ7ßü’ù*Þuhic¼Â½”@‡N_€upÁ7r‚+,"³Ø< ¦I_ÓæOÝ€ ÝÂͤÀs ?HÀ›µ¯Æ'yéÂÃåÖÚ8À{~¿n'ÅeKÊ€¼nÞߨ³çÖÕS]Ÿ£TƬ–e[Õ‰Âõ,Z’?Ý:'tÅh`ÄX  ÐϹM3d–£‘@;ÀøÃƒá9Ó `{G§ç•~õÀAÆ#ÝäÓùÄÎÈã·0Æ ƒ =Ñ9ìØ' ƒ½gÐ2žðž'çó6‚õÏ:ø‡qD_hñIó@Cø8fÜÞ—<Ï‚µ"p¡ó¸såqKçJ.[|±Cu_yg%ƒ7!p» RŽyЇ’]yá`¤î¡6 3«€IG6¾4¦•´4 ôD_ðÑArðÉWŒýŽàœW ¡ñ FùÅ¥^ÈCÊœ²ÃW4†·r§~pÈ‰÷ŸjPQÇ9+õŠ”%I¤ç$ =|Ö€ó' †žð!Ÿîïñ¦?¸Î‰þ´Ë8nôÇpxš¾á6ˆ*ú!½ócÀ#\zÈ_ƒo†á·Š8TҜ貃>å8ò ÙÒ7î¤.GÙ_ÅÇõ6àö h¢CçÖ`©kpƒc:Ò—t f\…ãGn|8‘ÚP̧!wšÐ)´Ý/„9~ÅÌÞŠ7Ñt°çÔð#E:Ç,¾y ¸†wJ󠮨_%­…ºED÷‘E;ô'-e1Ãû:\!EóYƒ³ä¾¥ÿj°øUøI²lÝ3ñ„®§¡Ç?Â]Ê»xÌ`)«T³À„œW!y£uÚÇêàu3A3.Æ€ŽœN’ž0Æ…½ÃéP[ãwØ)L\ÿ¬ßFU‰·4K>××q[šc„(ä4r0È8– a†RÀ3@¾P(}¡ \0lȰ!RaÓŠ/|ÐÇy/x3ËÇÇ01‹Æ!›<ÛGyý c±„g?<ØpYQðÀA|ÈWöÍ1²ð¡ "ãŽ×Þå c\3j*¤dNÀ£w]ƒ¢Zá8Ø×ç<©ëÒ—2ßÚÚöy 6+¸ëZ`õÁ³váâ\-_èâº=דäÃQà8_LÒøÁÝi÷”0ÂÔåÒÇõ¡‚ðêNðY©/<òS®x ß=ù¸ÜÛÀ@\ø¹l'¾„1Ì„ “ç«">¼*mI»êSJ~ð _ðGWƱãFp£'JÎ9‹¾†¬þ¬4^á;5:^ÇH“äѯ’! CÛ¡Öuëá ¿ÑR¸™/Xèðgi‰G6¸N7À–ü;>Rê¿óbÚBî<˜é2‹7& ]aƒÖ]ä0$txdtŠUž;¨Ñ l­3í©ëÔ‘ ¬0W¡80ü„£tÃfòàäQ·¤¥m-é“.ø(§—øNȃ›v:Ã7I0BqÙ7 zá–2–ñu8&¼úÏR§b1׫ëÐä©?âj7æÞè@àDñyõ¥•Ùq6çŽ]…NíÆª¬b{¥‚ Sv¸øÀÝé¶JcëÎa8Ä©>1`>Œ F£9 …²Òài~ÈD¾~nò½‡Ng­0r1J6ò’8>qöÞ1Ú6ŒÂg0à‡Pé1æ˜'ÂÈy?><Æ•FšårÊ™üØ\Ê€i9³}®L&ßàáXHù„î†ppª(Ë„?³öçϦ½Ý½é… ?ºp¨pçz}Ä|ø\-᥽*ç…³ è)^àé$.3òKDw^?/?Êyð¤,¡,‡‘ ]CÃç5»M¥¹A'™áo_ø}t1 Å[$*:V™4ÐD·Ic¸9D~J’¢äOxèH[!Ÿ©óÔD„Ç4×Á€øÆWZä¸üZÞRކLܘ®pköγi¬WyIZç +ý× w¿çHdÔ/ÆÝIþ#=ýàú„Ç:+’Âác™`\‚E—äsÄ#<À#gäQ\KÁ‘¿å‰ÖnàaÈšxxÛ/¢6ÅËý‰kã‘‘ÄÑoz5Ž•²¿¯¼®ˆMšJxä1€QnE´.tEúŒj é^Ãøšn þ€â éšáÓÖDkØ’ ÅG|?_ëd­ƒ]Áïu€×•·ÛDÓC}W‡¼1÷F}ðÁñw¾÷½³tlªueD³ ™t%¨3vºâtäžE·88àÛP+ìJÇe2tîàØ`ÃZÒÕ™²5`:Vô xö+ãêCu’‡¡.–ÝøapqÐÚPɧ·!<=Š:DG|Œ—1‰jøhÖØZ}(Sþ:nÓ3y aÒ{»é ª…&ûr‚DщCÓké‡wǾB?dÃ'ýQð_&78oÅOþÖÂÖ™¸ôV>¿º+ÊSmo4·úV†ª3¦lH…€ÏìœJ*ÃRKì<г Tº“àtØÌ—R±Ð™'­àÐz©N–R¤Á° ŽsgoFµýŒNGšy‹ŽÁÆãoöîé´©( ͺâ„4?¯­ñ:49Ìo 7°’¯wöÅ ¨„L*…1j¤¡›ŽS(¼ PFžYAÉöÆôP¯þyà#Þ™ñc¤u§tÏ–øx+@«¸]Íö¡?k3ëCÉfë†KŽt96–Ê»ó+½„àrEÛ;íœÅ‰â¹RƒwªAȆÜÆÈ@„2 \ñɼP—Èauƒ0eìaÿsï®uÙÓÙ‡Ü@»¡<@0ð€¢ÑFgpàÅ!A å\ÚMÊÜ0É»æWªóu=ŠÖmMiq£1'-†>u†î„§í0(0.üüsC,Þ1éÈô7`¤+zï®…3nK4,ôžqw>„ÚðL<>pÂÆ‡_+Áqã¶´„CGÜ4øúÍà‹¸ŒVø„ÆiE\8àñ‘o7m3òIú%ày)M€&‘$Ôénɻ҄4m:ÙJ§Ȱj Èk®2à0Ò{Z ­â¯/ÓŒ8eä¨íÙ ™ à wÊàÅ/jÿ®—pͨC;Å ¿è‚þñ4Mã*¿öì˜V¸áÙë©Kûõ¬Ó«ë,õì×A­7¤î]κåô‚o¤|=õVuÙ »Ü·D7˜N‡JÇMeÒ ¥cÄOGJ':vÞ ƒczñq!+NS!Z–Ê1ŒˆóŒLé':8ÇÅ1¤“`\ñg AgóWš z`Häû:Rà%c„#ŒcÆ ºl{`0DèIØ ÞÂ?•$nƒOÌEªÃ†ÖOñС¸V ˜AS. €±§.x¼ ÈèŸC‚ÌÚÑ/¯ ‚KY  KúJð}• Tœ²D—m9ˆÛ䌅tDäx¦/=Èïuk®ºµnâ-x74@†gûò}ˆSxès|Ä+†×û@Éúh@Þ8×°-Èd‚/âQ†¬r0ÀBæ¶ø“Wt¥ý ï\²ñÅø›Ž²LÄ«4âè)Z@„Cy ¡|§ª8£l¨òò›Ÿ·kÔÖЯHo—=|åRÆ%¹ Pš®~FÈ#ziPf}EƒO]:N˜_àN+\Ó.,mÓô†®þ܆<ü¤Gž%?i# °!ÞùPöˆ\ÐŒñÊóJç.;üŠ$vÑ‹HÂ]F/Ëlp{ ~•Ô˜xqÉKÅêï%XËÇAYÕ”¸w~#¬qµf—à—Á–ß å ”†¨ÔA—K4M×Îc™—ââ¿'°&?Ñ¥?Ã_‡»Ö˜tÚ…>½ì_B»ÔãmÄ»^ƒ°K°Ag¿–¬LªÏ*C2нÎàª\W¼4ù;þX3¼ßeI•‡Ã€ôyÌ¿6y_ ÔÃÃÞŽ83::(8O7VÑÆ8'ŽHøƒ³)ã„§ÃÇÕ+pÚè«ëõÄt¬¦o_õ+Yt´5;äð Cãµ …ÁǸ —+P?>pöµb\r×=ú`˜¥Cƒ±Ý0„áG>¾ÑOrhàRÊàX8¤ã §Œ°B`] cEžpBNâ“~®™7²Ù‰Á¸Ð[›¼)ã«:+^†où@oò´³ÇiþÃéÃ*#‰lÞXàP o ðöÃÅù‰ü1¨°lÝK€a÷¶ƒ|ò ­QÚcgE× ¹Ìˆe¿+9¼ò‰»¡ïGpÐŒ&å‚1¥¬øä®W 4H`ó\o,p`3í‡k”Ÿ+¿~ S4¬¬@GylÊǧý¤Þˆ£#ù²²¬¶ªœú¾U‚S/à“7ÂèI^Ñ‘{°0ÐaàE^Ò0û* _”i:4_V¤ÁL7úÒ‹•+Ú3:{%|éŒ>õkaÑ94áx5Já8Ëh4À ßÖ<Æøö´.Or¡o¼K‡Ò:§¸ ŽÝšxx$½ã_\‹¯Ã3ø‘ûR¿á›¦8„ÅŒ*ÅúØþXŸp¼±>;‰à¥m'¬2é ¬ðÖó žÈWÁKúŽI³ð¬ ZÊRçŒäÝú€ÛžS/pÇ™gú±LÇð ór„<"çü¾‚¼Âþõøëö@~×8—ãŸY} ¿Ñ~uÏgeâ4i2‹ã„4³52ËÀ™TŠ<:Œ>0:C,3ô5‹'ÊÌ»7HÑ‚ÚÖ©3èðlN<¥€0êö@Vâ¼Àà¤É÷2:rŒ/™ÒÁ9hº†NÂɤÓcÄ >z¢ÆÅ¶ˆb 1À¡õër2è”?ó=Æ?ôøÈ÷™ùžåË÷Öt²Q |³WŽ;i Љ¥ö‘Ê8a y‹A@͘yA3Up÷9O rãœ20d|=4÷eD,óKFNCß‹À½ȸ#ƒÎv„géâ}mÁŽÊÀ28ò Y2Î¶Ë£Û mIð¥È3Õ%²Ñ‡ õÁ€áá»}Ó¡/G’.'Mÿ3 B(?¿É 8_…¤0ÐÔ-Û§*bª|€«¬Ác•°à¸^¯ŽÕ×ùm|ñcø0˜¢¼¨‘?®|fP@žlôEKžì¤'σëc¯<Ó6øñF:_»¦¸ô¢ý€‡Y·V\Z0µ13ŸðøRw¦k¾e)—°Ûʇ¾ýfx¼§hEhZÓ!³¿1,¼Š6Y-7žð ŽùVdÅ?ñ…ïhøU¤ëAÔ|©ãÆß0À.ÓÄš]zôå†>ùå¹£Œª%qå&ü‰‘Zå7Ãkˆ+ŒF³Ð7œì“FžpkðËJð:2ýJ£ò&¸øŽ :5rgº,ÒGypxÆ ë|{â¯o yAC×{«ÿ¥ÆW•á OH²[_é:±Ÿ8S­Q÷×oìLOŸTǦD¿›.ƒ€!¢Æ1s¥<»RGIgHƒÀ°SÈ1®îDaÚ § #F#‚O:\sâúG'ÊOìÐÜ0™•çÜw €C}@©w­§6ŒDáJ—̸t(轃 Æ80ˆ|Ä`€À`H0îàad|Ï^…‹íf유?à¢'ûèdBõǘ³"€!gåƒ8º(9ÆÓFÀ,\Œ:Wðév1<¤£†Õƒx6G^É/¸Ä³e¥~S( ”åA$`¸í4ÉÇxâ6…‹Ãǰ¡¯Ò±V%ôŠ"‹ È?÷¤<£ìh;e¨k`FS‡”3~ÅYÁàW¡e ³­|‚³%8oCPVÞë—Î6ÞÊ?ùbÐÈìŸÃ¨ €XmbkƒW9Ñ‘6J˜óÑg4êHOÃàM8¾Â³¸à¤-Tþ­ëo·„9Uý7Oø¯û72{º±ˆþ ÞâJwhmñ_ujРᤠ,VøÂá3:x„7-¾ÂYQ®Bźÿm =}äIøU\Ã{v×iÁ·7ä,Ò–¢;ÿà‘÷¥lÅ;Þ‚:ð|9=<„,|:ÉÞa_ñ€ó©<¤.\kòxð®Ì¶: Mpê¶+‘~µ„7>P§ô±fв’úP!ɧc»~ƒ¯èÉËhyf¶É•¼ð¨Áln³¼¬óÂefåt4ˆ44¥aìéD1–àÚˆÒÙ©3<ÅÇFk:á3£gOŽ×•‚šÍ˜(hãIñŠ­t*Ù`~8^ƒcFî[ ÕÉwc*bã4ÒIÛ@Ê@  0Œ2Ɖ½}çG80 /'Ùqö‘ñѪöÚëD?×øR^}øzÐ!¾ðÀ¸ã¢>Fåv,|Âàæjãöú™5K°Ë…Cˆ¢×©~hÒØ>Ø_.!¢|©WfÁ”Kà $ÐãæÍ[Œ`´I‡–%ýf09À ݸUãJžÜ”Ç÷¾ö¾h¶3&}<óèÁ£Œýikƒ[ÐÉch¡+äiûž–í•¿'OžN·µÁªù8j‡묄rÛê$íÊå+=\OÒQ…î6ÀÀelÔùæ;RØuîzÇð˘ÓfØžÀð㣫uP91!”aÿ”žzIý¡KÉ©:,þª[µ/pÇϽùFA'sTR 3nK/ï2¯}Ä]†+µý 2H+‡Žvñ“D\¿–¨@ H‹w(qêã ZžjãÎøTYu! „i± tî|Æo:Ì‘/Ç.åg‚ —t¾*Á•|ÚiÔ§,ì Ã%^±+ÿZ·F3†Cðeºï«è“7—áà'Ë|'þ%ŒòÊ\øt±5c»Dðzo| ÙŸ«„Ž”i߇KçC£§#ÞÜ䀳â2n¼ñ@‡†a;>Ò’­ ²Ä©%o-íÇ@b0¶cã…×ÿý0 ÇFRi6ˆ7¸¼ŸO:§ W§ZyxG_¶èfXö†Ý-û¹ x[@Aë®!eb “ëå<Õþ=y@®—Ã[>¨x4ø–/£‡@³wt% C‡¡Ï™ t~<ÆèvMaôÃhspÐÁ1¨^-Žà¢Wë Ü =8%ß4]fï”ÙKÌp9/€Êš"q‚ÿHËî{:Ù¿sMÛÂ=Ö=¤³,ÎA½ ¤Ø~`%À+ 40»ÇpïIOáP3stbÆ|çîkÅ g0¶8^Ü—¡‡ÿ±Œ(W?Õ–Ëñ8òÇ Àm-ó3ËfÕ]©òÅá¼ßúæ7=˜º¦ûži0°»wê7 ž‰OVRX‘"ÔSê"ƒ65 ÃÙ—'£}M êöL+Ò‹0o™P&z 4O9zæ¯2$ýDetêžøŒ©i'ÕfW†>íǾp¼ºc™ªÑÐWgŠaÚyÅ+“NرÁïi &鏯¥"íocíºÌÜšøˆáçD#¬1*/Ì|:>ð–Öa2ñøBãï€Y°$ÑQáÔv÷Ã_äéktëbi—Qêë„;¨V¨kè]Æ’Ÿ²N|¦o£ƒŸu!>êœpüQ1Âkä.Q~ÝãÉû«è9+Ë«T&ô½¸à¨#Ì5)ã!­YÛˆüÃo| ,?U‡u #³K&élñé¸ïܾ5=züÄé¶fgt¬,s:Lz¿] ‚´t¬}Ttм^ÅÒðöãŠ4P¼žñÈgŸ>楂§ÓıM€Q­H«ÁŒ«Nýâ€Y_Á½ˆ¡â–#˜_?“èŒã¾vô¤‡Ö §T¥“?Ëiz%l£ pmβ0Ö60’?~‘':òMŒy42f6œÖk~®€²a€ÀV :匀&⇃יÒ$€ƒÃgfϾy­(œÔBñ ²Ÿ½u\×übä_ȈK_hhË ÈF—ºL9A‡®Ì´1ž¬&ßÖÀ‚Am…d°¼~kï¶‚¡§~h|!|Œ?p>ûä¤Ù?Û”~ê™ÿ‰xSvÀFƒÍ I D£õÉŠHðJºüYX83C/ù‰Ã?íj g ÐÓЙdÐáæ °£$:àrñ ¬tåi wZQ´Øv^I»„ÐÐ5Ž0ÏÝ R)äk‡†rh©ø’6¼G®3œ!âàï´K9$ð Q.qKœ¥.‹ô¥˜Y|‹Úÿ Íùt™×mnI³ˆ§lá…¬”gx§ŽÉÓ%i­ tØWÄ%¿KuÉ'Îe3Öe/¥]jožƒÇ—á•>j¿²%jÛ¼øÕ~ P'·_hÖ²/£ûÐEk|tÄÌô˜Ý`0løÌî1ø:~vZ¯±Ñˆ®8Ã~2¼Ž™iqê\÷ÂëökÌFÕÑúp\äÐ!ŠÞ0Ƨ8x²üÏ«Ut”?Œ-÷È»¡KVfˆÖÙ¢sg*O 2=«SºõƒòÂÒ.q–ë©xŒ)ò-ùðB?¥Žqv\´4tB?aô„Ư@¢·ðY% Œ —™"eI<Æ%ƒ‡ ÊX3âÜ`¸è7DÃà‚÷ûቃž¼`øàË;ö·4pãB"!ÕA†#}zÆ;÷5!ÏЉ–ÃvLJZ½¡…?òöµUÁ׆„ãä™ÌΟ1£Ï-„vŒ?göÚ²=ß4ÄøÖü¾ÖÉ6Ô.žêN‚[·Êx³ôÏ–†Þ *,m*ÿ·ÚC3â8µnÔÑîÞñôäÑ*ktÙÖáÀ;.o ºl5¸¢\FâKùPOvlÕPŸÔèkÀ™òt]«œ}ž€º-m,+„=ËWšáÂ]á”áGe ¯nä¶î' vp ÒÖXË‚Ó ƒÓœñ‘ŸxMR*¡c÷À ¯“< krimì%°Ž†Â­!kÀŸŸÜêoµå%¤óêÆ£Ó®âá9@æÁ&³7[àj#_J/ÂKj,yvæ z»4È ¯ÔÓ‚Ô²z™‡®!Eï\×ÛõžÓ–ù¿b_‰¿iW)ÛËWyÃà/ãWñüªºHzü<‰zþ –¹G’_«ß¦Â¯•ç’™¾Í²ñ©e}—ËgܘԀXv¾~]K»Â¦Ã£;LjÝŒì6°w£Ó¾¥ûÙOʘoïð59 !†]蘭ŸN‡ÇêôSwìNÛ¯¨“ÆØ¸¢Ä‹Žç¸*4wØ ªE<*l:yâ„q6þZcGÿ–5/Í*W¼áÀÀ ³gHOƒÃ £ ýÈâ½cW´¤CMf‘ Bz{Eé¼!ƒ¶%XAxôùg.“\ŸÌP!8¶\n©•-.õF93:UÙVû¬Áú‰©þWÛbp@»¢ÌùAϯ‡x‡#xÚ'õƺb_q:æ€E°Â+‚Ša½âR®DÐTuaß)Ч»1,à, ¤–E§Œ0ÏÉÒ°w¥Ù-ø+œæ,NÆò Vš£[ÃsLŽÎ#,|ëJùŒáFGé¤Ák›•¡* Ç© Ü‚W_6`T ©àÓhÓ™.¼â¯˜%ôïrQnÇzIÙÏÊl,Ê­ÕKpÇd‡œ¤±Ì lz®?ì+?ÐÙ¦“9ÙÝ{ eCúôésO^d¿§ƒaÌélë´@ ¯ŒÆÓ¥"ƒÄ€€-í·C’nÀÚ.ÙÔ*³GfTtŒÌúx ‹n…f̦pÙCe¶ÍCƒ§sÍCLàÅW ÛÞôÊAÂ\pÄLÇ«„q½ƒ7Ÿ%m 4 ÊÂzˆ7Æ…A ?<0ä>èe™è¡_'ð±‘ÃHË€ƒÆÐòcÀ=ø"ÏÌç²V:ü0¦1Τaü‰³ï›öDëÁK£Ã¨áàÁž;fÿ>Œ§dñª"ûí2ä=y¯¨ÈèBƒÇ8ïhùÃ¯Ô ƒ‡w>ô–{ü ¸þw4þÈÌJœ³"¡yæÔ=F—z'Ï” ç nKŸ6æû1oßÉM„»Ó= |ò䉷(?x¸Ü[½ñ}ˆÔåìtÉ8mËnhb¼áÅü%<éö› ÚIð¨~j˜j?òpÀš_^pHRðÒÖ<À.œ Á5L “˜”^‰W‚êÙÝ)<ÄœFœ:.YℸÁgô¤5þ“—’R!£ðgt Ù²w4 à·Ô;´I#Þä:˜ô¥ßð{ÞZzäšö žKVÄÉ®ô-…]vGÝ–z†$q£>‰ça”âß`ßÏFk›i£ñ“mâ.'ü…[Ö{Ox.ù8)ÿðÉÛëêTè¯7ð6V0¿°Úyä{¦DgªìëðßÙ5NS×^ç®öx÷Ÿqõ¬Œ) C¿yë\5 [¢ ]ÛKGÁ‡[Ĉw¦õdzøÓÅÆŽ_ #Î’óÍ[÷d(ëë{©:EwÙ£“œ…|ã´Îš‡¸ ƒvtÊkÇÌŸtx1`V‰ÑDZ"ÇLcJ-c }ÞArõêœùˆ7¾þØP`ˆcXl\ÐM¼Iî=jÊWpåÄ`€WôpE\ø`Àón=³àèA3Ç8 `à Ÿ¨Ë X1¿äG²¡Ë!½cpó1@ν,¥çFAÿ18Üü Ã‡ÏõëìçkY_˜Ã{tÓñP»›ÍÇ£üXG"—lGP¾–éÁŽÎ hÉŸöä™?w à_×¹t‚7ƒÆÛ\#üÂÉgíÉ×åB´ ~ dÐÑõ.} oƒ&Ê-õ…Oy!L› |¡£,Hs¼ù W{R½ î0þ€«, “8󷜄 ¢DÈrnð"Ò` H@çÏÈ7°™¿ÆËðð‘l†Gú7|TVQ¦ô›1s¤¯„¦¡€Oà:-rž• BÇ(Pâ 7¼ŒµHë<`£_>ok¸pÝäÏ$„ÇHܽl¢güeúÚ%hÌ?aë'~£.3yK/Ñs‰úUŽ»\”<§£O¾\‡ò)«”þˆ8ø¸ YïÕ­œ|èL¯Ÿì“àv9ÖA#˜Õ‹af¤þ7v³_à­ ¤öèÕ—g~êq¼ÓÍò°L›%¿öukwïŽfŠ_¨#—qÐòÿ¤s×ôæËÖªþ+^ƒŒkíõÓà9É^§¯1ö îÊÈ0Ó}òXÛ*º€Ù-Æ#Ì ‚Ù_fo½Ó]naKI%g&®€ó@£ ·Ñ_W¶šƒWNÍîËx³ ‘A æ$–0àz™nâèK^胑DŸ¬ÄÈof¨Úƒ1>4Ùâ ¼¬ 7‹Ù”­´5Ox ©óeVðSäÊ2Ëç\†»—¡p¼×/½ÀeeƒÌ’8ïÙché˜È½àKyp†ÙÇæ³ös &>ÿì³é=­P¶z¬Ì‡qÞ™ã“gºÐHzá(7ôr]30PœAÒ àîÊø3xàm†#áð "縿♬x<úüs ¦´5"]Y•€ƒ:øSwvÍ'Fþ L>´ü åçxó¡Oï’¯\ÂnÄCŽñÌ ôðß& 0ûÕ*ë/¼„.8-boü3Ko 3X“ARt˜ÑÏ"¥O@«˜BF«R*ŸÂv`¥÷ŠNiЩÍtê怓|ƒBjc]|š#ã ðÒ©0F<ókx„qILÇ8…Ø0‡4 ¡3Κ?áŸ$㋇áC™t<Á\^ƒœÐþ¦û´ú›”©Ë—ò\pÒ–À¯0ý¦(?;c••3dêÔóœk«j•+oFé“M¶véYVõïÈø¹­V2 A“„QßDø­ ¸ è®n{Kaâ3ܽ±==Ý× P{ú/tòúÁ;w5Û;˜>ûôSœ´\øD[2挸/@Ì$ŸóûíZ8;eØP³+ÊÒ†DÆŸ%xõ­ªUŠfy/fÊ[zkcľò™ûzœWÑÔùÃïÆ!ãä !Ž¡‚9®5:á˜ÝtàCŠQ¶±n<0pÐÐ~bøA‡Ž-V Ò,SVÖA2™1šôâC£õLTÆ \ËVšgå’ øÐ¡¸„4ÆÀ'2€c01ÚÀ £¬ˆ3»gЀ#ÏÜìGI <²2`Û!Ñ9„½â€<ü)ã50Øôá<. âÄ?úಭ°½]Fö³O?™î?xÇ—ý<Õù€¯ÿÖo9à8v£2Aßä›<"/åFšäI ¦EÀÜviIlñ Âj%™pHëaHCßüò …š¨d8ÓW°ø$\‰Å*𔓡0S>£Ë sZI¬ /gÜæ2“ ´þ›©Á‹x%ÒÜ*“Ž.«–ŒÔ™.ÁO^žÑôg®¥Õ™Óø¡ È´­œºÌ¦e7sÏ€%oi —EÃuÝ4ýi{¤°ËŠ&ßkÿãÎËŒ~šgÒçÂ/­8o©_Ñ,ãœÕY}›#¿¡¾ Î¤éû#º»æô”ϱs>MÛ’;¬*b°®pi-¹?KIX´µr~ùë¿•€ŒÁ'îôÏd =Ö¥5Ôëh_|ñÄ-~g[35uΜêÖíAžÖËʹÌçÑdù˜Ù<çìtrðH§Í»ÁöC¦™š^?»±«¯ç)Mw+•λÀ€× O5RËë|¼EPç$Z<Ü¡«"1îô lnèzŒ8 \/ÏÐéð¢Iq í£jŒÁ«Ã“ècCòCNAÈa cXˆ÷YµÂÐxY_>«àQ† Ð jøaÐ(\`ä3ù‚afÍа„ÎJ ·'úò#ÂÒa<(èA†p™É3[æ|¯ÒÁ?ú²ÿ]9©NèÆ`âH³st†ñå<7~üñG’cúBFžÌ–>2t,ùõæÁ5¯±%ôôñcÑè-Ѳâƒn¬BpšNØWüj@@¾Ä ÇY# Øxª×S s0íll¿èÆN +„ùQN”ƒ¢ ú¨V`(ßâO‡R%‹ß•h¤V-ÐÑ(Öðì/´à* wó"½S;XáÌ\‹C3sŠ/ NŽŒU«/yètÉ…÷¥…Ï_ÌT§î`tTˆÖqÔžF9¯ÓÅr­ËȬ.q0Ÿä‘ˆ1’oó4ÅüO¸¦.ÌÚV÷]ÇÆ÷’¾ÏÙºnŠ^éC\â?ö½Nïá+x‡ïo’ïzL™+cc<áUy©ìô s蘷–˜ í°­ʳ3Ù­…òzúéÉ¡V<µ¢X‹Éz Y_w•-™Î4ÁP3i ZŒÔçÔ„ccSo+ݦ7ÏCµÝ+Ê?õ9+t_?Mˆ>˜¥½È[¨#ÿ@ä‰f½šÛñLT&w5Ëz¦å™u1 ç´?…»£ÑÓßAP'͉î½[÷Õi?VT³~mhÊ.£Yì] 'ë™q7£ªÅl/3ûì'Ñm 33¿¡Õ‡£#ÑheuX%8—¡©· ˜)Œ›à lü%‡Êäg#Ù*Êùi•ƒ1 ò0bëÀFApϤ%,Á…Ã@ƒ%$–…|À#ïi¸MD#Ñv”€B >F4:Éxaˆ1ÞT' A'^wäFBoƒ( —†É#t\šdÝ•Ž¡ãÇlcŽçð^…èAWõbd‘'ë¬ô…8 b`óé_Pš¡ r±ÐgâQÌî¯ëà¨oŠD'å…ý¾–ë•Aéwî´ýƒ}\XÎçœÁ îjÐp÷^}€Ã~4˜¸ÿÎ;.{øñzâs½q€ ÊÀƒêSúS®äüŸŸëÚcFŧ]îò)Ã^~­^ˆóÃáS‰ ¢pÁ܆?® „ÞüŠ[,ó5`à;+©Òàßâ—¼eZt©eH0V žplˆƒ SÜ–Ò…!ÆðbØ­·ÊçW•_øgdpÀ ÃÇìcÎ CéFìA†ë¬n¯ãPt8 äÚ>ˆ@~ÃŽ=4bTqÐ~òᇾ¬‡í î¸{¿NãsrŸ[ú0Ôwnßñá@Þ8—~èûø‹GþL1írå®ê žôôFòïݽ§ÁËs¥íNµJÀ ˆë„5 }.Þ¸?úȺCÃjy§ì(O\ðt>) ÷¹©òMúM¥áŒO©ë¿;qñˆKØüIƒ6²ˆó¯Ø9mÖ‘$!t©%&VüV± 5ÚÆºÃˆÏdbÜâI^M š_Çé´.ºÇ’áC;Ý,¶H/é¦%¾ÇüœtçSÀQßOõ .üFzãeXþ °¢LÂzß|›¬`@Ç/yèz6]Œ7È ÝZ}zâW7ºZåÀ¥£çøútK}Äþ‹óéÁïéÕâÏ}­<ý«Î:_>½xÆÄo{Ú¹y1=~Nÿ¤­fMï™ÈËìO7µ%|r¬{Jà©ÜÞÚ˜nßÝ=R© #çŸÎ¼  +ßEs*»Âtβ1hP¡¾H²9¹~  ×ñ*yÇA‰š œjõó‹Eòk¾•€Þæä¾íÍTívoïúôô€Ö°_²£bÚ À`oV§ÉÆáóÍhŒ¤6d´UøÇGŸZZe áX3yVÔáé»üe8q*†¥¯Ð!‹ß¶jmóŒðÊ0r-ry¥ðBç <{G¶èoì¢PÓLüwóޏ×ABë(\òµÊ› }3¨~5®=˜šØš§qÕ ‘Ãl‘U2©ø2¸¬ xE@’LÞ ~Î``ÀaVŒAϬ҆[ømèÈãáN=[Ù? ½Ói¹üxP„%гR€LøÛ¨5}_Á1ž1dÔ#ü8‹À@c†nÐa8ù¦Õø#‡ Âþš¡èГïpF€D½{ÏŠÚ‹–æß}_whE3Í+zl1À—6ÁJKþ,ã½8x®U£ñ“|¥q⟳Ü3ñ…*Ãz©¼oŠþ™Œp垸xWÛ œÄ ^|6ðÁõ´Š^þ»ÀBk ^Ò»n Ïí½…=3™® >¸%?—à‘—r2ò½ …?Ÿp»lÕCòk½àOÝ4—:7ÎOúo®/ÍE_:î ÖôüÞ×a^úç]­î}øÁmçêŽuC;×¹î[ƒgÏf5ôà)&OKúêÓ)Oú€ ]EõgsÁ%g[Z=&B‰Ÿoê<&žšdk2ʳOß)Ñò5Á «›ÛâáÛ`Ùb­¥oXÖuŠK=.ÓÕ·<ìéþºãoe ¥d?Wÿùµd žU‡ïžË àŽ^è o7õº—*F5 Ò½šñŸ«Âù@‹:ìÃp0#Ãq-ðù¹ Ôï©óá}VV› ì3ûãÝnMà Wv¬¥[*†åf*±®æô|™ÎŽn:dvkOÉXÒÖìÊ ûE§'ÏÕÚGx|¹ŒÞ&>®T7¨2"ŽçᤱéÇl†—‡ÚƒÑ€–=ßSçxø1øÙ÷öÖôJC³Q“1­ò«Ævxit4= ´—þÑEò1^œ·ð DüHÇàF× (w^)äz]ë%ZŒ4¼‘É o1õ@ž <È‘ŸFÏJÎů ª>ÑAüÙó×ËyÚëÿÜ›2ÿì“OõÎþ=ëĉüZ¶ç^xøà£äÓ8µOqY?©7¶9(SÎRpiÛJä*çý§œ;aåG3µ§OêüåK^Ø:"_ú ù—¼1`A¶¾d3àK÷?÷Î.ÓõìÞéDÀÂÓ_~-\ôŽ5˜½n¸àK®i[rÂñ©gsmrV\Hhò:°ânÂñŸø6â–Öbë½%ŽtGÖLF£ìz_Ò5œ’¯¿Ï²,Štàþ2>gdy°|øÇ‹¼À^Á§>òìè]»‘oÊe3ÃÁf~ÑudþsìõúöǶ¾á±qkz_‡uŽÔ_¼àúnÍê/téTýôÖÙôHÉOµÚ»G?ǰz¥|{çxúìãôŒë» ×õV™ì ­V½„\§¿¦v7ÔÏÑ?<×óÎZÞ…l  Š•I T±öêÀÅ…&&:ÝÏ„âÆîmM&t7ÉŽŽ"sÚ_ýÔ–V¸¤mW«Ð'…¸ÏXÔźöПYɤ¾Õ¯ p-‰.è_gôm Ž•ÁOÔp‡RMÐÉrè‚‹}hÔe¬µÿªŽ˜Ù{,®ãSÍH5ûW«Š(ãz¨QÚÏ~úÓéž ïsßÖè/Ëß'lˆžÆA„â¯Y² 9ƒFŽìõÓyc”vÔ@NTÇÌôU‰žªÖ9œÈWâ|ñ†y\XÄêÃ…AñuÆÊ+¬Fãa÷,œ¼Jgd »r˜ü§Â=’Äðƒ-Ï·Wȱu€q‡Vâ¯âeŒr³€Š¡ò78É©Tá!Ûæ¢AGxÇè‘Fù9Yü1nà°eÀýù䇲C&?ð«<ê•Êð÷ AL¬· ¹y1y±’Q†·ñÁïô·=ê«„Rª•w `xÝ~ 8ü‰1ß>ÙžØïßÕr>¢XÞç¡FoÔ;yd°ù…hÙ÷g›{ÿ´há?N‹@IDATÑ‘‡–ÁŸfð@:KûðÅÏC£Ï‹t¥<3€3Œ4ô ñ+`¤óÏ8åÝ€­Â2ÂS2Íß”õ'ñÆf&Ûì$>=} u0ò™áYæyˆ“.}ì7· 5~ƒî)PÁã×e.ù¼ GF|˜\á—äù‡7 +\ÇÁ}9 ŽvçzizÞ‚Ïd­ˆúò/.´…wws:<à¶ÒÝéá»üÏ;ïÞS_®%ôãgÓmmý>{¤í8¶ôÖÎm-ë?S_A_¦•ijgê+Ø’ã€1ßüÐkãÚï§œ¯©ÏÖB€úNÝTª>c¨†Ëå(|ú+ªw[‡Ð9Gt®‰à¦lƱîŸa ÀD°®;W­¾ääH—™éæZÑÖW×J®ú™‹ ½Õ¦³iÞ?¨É£ÞÑoÕ*VÉ} àììsAk6µJ~í¡·5 cÿÈ ^™§“wÇ,CvçÖ®ö‡7t@û±l¨h™ƒ°¡Sÿ,çsXO2¨;ªxfw\óÀ—ϸÞÓ«†{·îúR×$¸ª(µ- Ëh©rØ+×öŠ*¶NÓ(غQßЈ £‚ñdK€<–w¤°–¨Ï§C Vh [Ûz/ý¬Б`¬,jµ8w°tŽ‘KgÌr?ƒ<ø)DPD\ik²tîpè 0ztÐ}7"ðõ/ôÊe3|–Ç9»€áõƒ!^¡A¦˜ye,‘Ûà LþYµ ð$/€ßÈà#§û×,^´È®ÛÅÀb|GãO9›—ò Wïr¼Y?逾.ò$=€<Ñ+|€ÁÖ“GÎjÅ'Ú³ç¬wHPלà·ñ×€]h/ðeËà©öýùfƒhëfIÝŽÈ»þ’ËØÁù"%éÅÌá:"ÿ£ßô4òš°óA"iͯ¶\u(°xݵð3Ÿðœ1˜|µ”Á |¥µ5ögŽÑÀKÞ{•~´êÜf<›À%Œ<噈RÝ(Â|TôŠðŒÇR¡wžÆçgG[nòÃ?í2h3:ðËä5rÝ䎜ž>æÍÈWüyU¼+Èß.X†wKKög7tûæ×µŠ§|Þš¶wÕ7l0¸>šÞû®žÿ'7§O>ùùôõo|Sý+~?S_¡ÉÁ‡O½ {û®ú õ‹_|þ¡ûÖgO{Ižgt“ Œó^ÛÅÐKBËb›TëÞ’ÏmziîT“Lž¶8ĤÞ÷¤Á7Ù¦öDƒè$Ÿöqý×Ù§6”Ô@$|G?í‰c´íéb¡¾ñCÅ/%uœ×xkÍ¢~Êl Gglƒ¢‚c–ÍLW“‘×SíÛr/Kį̀9©ySg8™Í²3&³´šõctéÐyxOœ8÷Ü¿›Ó-}>vC†€ë©™±ìËÒ¾gÿ4Œ³ôqgÎÌ_³æ¶ cêY±:zŸIð²2BHÞ^¨âÛÛÓ÷ 0ŒâÏÕ|Gžíö’®mé–;­DÜО6Ý"ú’?üT¾~$Ð(úŠ€ M:ˆtá ‡&†ñÀ8ã» µ88ð±%}0T„I3_3kà!¸ý£Làm9‚{ÅŒ]& z”cfŒîð&=:7/Ñ ãÏãVgà€ËU¼¤mJ–NŠš—ïhÿþ–p0¾~v‡Õ.¸#Îq`Ð_—×8½g/|î1ë‡/ dp2:–N”ÅÑ–ö哃Cz.¬>q~zlÈ„ÞíDºBç·4`MÛsÀ§•E/çÆ[ =-8ð#\¥WaÜ‘”W)ÁOÚ%|'´??úŽàv”`Æ»#­ /Ú>nw–‹´/ŠÙ_§-xÎðþ£¬“EÄíZ0Ú.üˆ;ŸâÙÓšlâNÎà÷¼ ö2w‰·Ã3tàD?Ã’¿æûÙ òWÐßÖ³uGFq÷î·eô8EÿéèéùôÛß½9ý\3ò½ãÓ ­àî¿xär8:ÿhzöøzöu˜¾ÌùøO×…_7N§>ü»éýo|m:8|¦;Yt6L»êN§ó}õ]:Ÿµ­Âe…ð:ëòœÔ—q®Úƹ£pë|?í]ÐïŸiÏ„‘f{’îþ\«Ôç™V¥a·)›u}‡íhVõ±0m?Ð/žj{âeÎí¦!ŒaƒÔxµRËà»·6øÙ˜ЇÙ3N–Xا¶­½à›Z óeχô: Ï’ ‡Ó®µ­®¸åZÙ: ÞÌP?ýècG °*Àûß¼×Ï _fü®lf¹*mþ)oÈóêIžÕ«r}ÐD€Ù'×ÛHª’÷niP¢eg®¨åðƒ èX–ßÕ,öDƇÂñ±>È£3 `0$ìI38ºyÃñf46òÓ0XŠÂ!ƒÏ à뇡ö BúƒïŽF>F¨fçœ}¨@dÐyƒ$‡ãMšË]²á‰L\åWr”'#ÊÞ«¢ÅeU:fÙStàGþ¾Iø¾I˜ø 7#o.ã9ÐCÈ›8¶'Ø~áÕÌÜ ü‰Ú9¤É9ßÿ/80õŠ,^I„¨cäåîêÌŸò•Þ÷gö¯ð3­$œ¨÷aAœ¥0ly&_q”ñª)ê¨Â©Ç*GðÁnv‘§Î›¯J(%Ä!·žVí¾—L~’,±1‰Œð\ú¤Ó³áFÜ&ÏÚ+¿ëoôª&§#FãÙø[ŠÂ]ÍÈ‹é^°z•¨Ÿ!ä‰å yër[ºóýËÊí˜_«¬?YátYFh1dEö«dæÿgœ /sߟþóÿBG¹ÎvµõúM ¬ß™î¾wW÷t|{zç݇ÓõÿNÿÏßþûikïÅôóŸL÷/¦oüÓ›ÓÆþuÐVßáØÑ¶Ú‘îãxñwz.uÇ|Z\쎙Ш8.ަOñcM¤è—ô±à¨|ÛÎøÇs—ðÒ'=°1ÌM ççôóy+M+ ‚×d••Z!åukµ'¿Î¬I‹»@­žêà±V.tPÇèØÍ).9ä»m*e Q£_$ü&ý·6P'üddÌ4Vp_K@|Ú®NÜã?á`…fÌú´+Kîú& é0þOfÖ7v¹ENBôUƒôÙÇûÇ,ñ®.“aŸc„;×ã§IY)ÒÃÈÌÝ·*§–„–tüÓÊç0X6<Ó78{à†ÂǸS‘·õjÚ³gOÜ`ØoçÁ½E Y(œö‚ ‰Þx`E½XNÂÀ@?6Œ‡¯Ƙ(8x4,”¥Ç¤µ¾Ä'Tý$•¢«Ï«:r†rƒïhȈG|Œ1TÎÀ+ºd@‚ŽÌä•`c‹2yhÀÅaÐÙ œÀÓÎð¤Là>íÁç1dðÙÂ03`Pp]ËC*>àÍCÉ…AÖKqÎ*ÀÏå%y¼:z¡C~ Yò¿©SÃùR!oPp`\&Uvø¼y?Œ?q~ „ЙraPI¨òN@?pp)Spp®+|ɱkø•Zé^a‘0 nƒQñ&7é”:2)k»!½ÇI[ÂIl0ËY¤G¯u³Ö&ÉìÆM%¬dZ?_í|Âoô¡îº*O3yN$y]ä‡ää·û®ÿÞÈÇÅ'Üó3–y“ë¶ù2Ù0ø5qwÔ onýÖ´yÿé½éÁtãöÍéîÝÎyíÓûß|oÚÙüd:ÛÑ,]ýΩ>­ýõÍéÛ_»=ݺwgúûªOÓ–î©.Ùžü­žÛÓGŸý_Óçz…îèðsšSýh𰹩þOù¥ïÝÐlY|9wÆ_^éWâC‘pü¶ qð©W&ŠHúW¶Kµº|][Åú¤·ï„ÑÍš)¸Ofb¸Áu¿ºõÏߢÑyƒ“c&h¢WÃV›×ÿ´³´atÁåI§©™M˜ ãõÿ}kí©~,#¡~CG)”A \ Æ6ªìomŸë¢u´êè™ASø•­íúœ+ƃŠ:ÓÝþ%FcÀ^Õa øáX`e€;ß÷nê[ó×ÔX%¯²äCeÓÁ±„Œ¡e`r"£Ã‡z¨xÍ­Å…€.‚ÁG9 ‡ëŠ<êGùW W;¢®€©\‚ã0 à5DËIÜ©õ§ã8ÚèWp€Ò£80êeÉ{À-ÝT„606÷ÕRÐ?¼¬ç1¨â1æ3`ûý >DÂoµr!¶¾ð”³æëd¿$OÅå—ø /Éónê¦eÓHMîËÊô—üÚPw¿¶§Ãu·§o>üÖôµÛ:sð»Ó?ý¯ŽÕ©~öP÷dlLßy°?}ôéþtþôÙôèìž®ÞþéôÑþ§Ó·uàúŸÿó[ÓóÃ;Ó;êKŸ½ø\¯2?Õóz:½ÿµËÚŸ~ü³MOt³ëÖ†&`ŸT¿pƒ3^8m½UKsì¥ÜÖÀne¢W xÁ¿Ê—zâŒXU±êXªºÏEžzþsõ«¼b豂ÎèÑ hþª¶ >^Û\cÎêï÷pp£í´vád…óüѨè'Ô‡ý|é놽µ€2ø© þP¿]2OGÍ«aMï½÷ž×'‚Ù×ìŽF‹ÁÀƒ‡§ï~ç;ÚwÒ…2*|L(ç¼4¬þÅ }.WW «^õÓa4íwaÀŸ<Õ¬ð\ûXÒGúa÷¸Æø„å,áä Ù‘Îl ‡E<Ð-tô½®´¥“ØÓïžê=Udì(|]‡/xñ”‡…S¨­¡—Âü0n9€¦O‡T'Þk[!3RØTã.ÃEcĨ »\ÕR=e _ÒYY`À…Ák™ÆrÓ˜å#ÏøÁà‹Ö&~ðGg\~mò¢4²Ç.rY.„/¿Gz=1ðÖO«1µ_§•• ô䇳"ÁA¯gº úf¯Ÿ›àipÁ€ Ïë~5ÈÐÒ¿£ HýÓ ]zy‹Ÿ?`%úçG¾Ô:ñf0çÑЧL¨³–oã7˜€ÆÌßòaØA…è[**X•^aÁ€štL'µ¥9ÈŸ‘ŽxsKy‰Û‡ftÉÛ:^M¾õY—.>iÏ#K‡´cùzÏà—˜¼:À: Ýü†<:ÏÀa8º)<{&H_º+ò¼DûÕãúà•&8ÇGÿL“kÓ7¿®•¯‹ûÓ·î=šv~kúèùöô­ûN'ÏïNGï=Ÿ>û[Âýú±fñ¦ïþÎÆôôs]Þ%ãþ~<Ý:üÝ ù¾&*§k•îPKõ7o=œþ›ÿñ_Lÿûÿö¿Nÿâ¿ÿï¦?ú£?šþäOþdúÁ¿ûwV]ãr¹E»X“)Ê+.á¥Oz`Ëð—цné|ª½ })“,Õ%Õ©ÐkaÿÇ¿ù7e\dpŽ%Ï—îPqš¡úÖA½š©±ž. áB˜z5ïH›@\ÁÌòH¸ÜuíBÍ^OuÓ3Tî”®õJŠÏŸs`dÒ` –ÄwÔ˜žëγs %4›ÖjˆŒ Wk‘ÄŽÛìx_ãÆìš²¤H<ã@OÍøP6¼¤+îÙ¾ò‹3à¼5 ­z#ÃTÐHmì„GËñÇÁ›8`Ì|G7âxð¼\®8:²Š€¾p >>ƒp™õs’¿^ =ôV0ìé€^èÄ Vx8a–ݨø¡¯ËÁ¸¬Àh…E¼<ðŒÕƒm¶ ´"ÃŒG /2XþÏ!Bo](å ò÷L<\×åS\ÃEO»V®= âS|ÅÛ ­ÝÇ@^㟤! ]é×ä']z8ØÒ+²’mÛŸ¿ƒµüÖâ:tü°ä5éUÕY‚Úu'Òämd­áΫ¸ð_‹+¾Ö‡ÄȈÌèxÃé³ÿµ >W¥ÿ’ð VL¿¦ö}[†ÛÚ“¿3mßÕái t°î‘®º½~úlzÿîÖôèà™úµw§}ñ‰ÚþÆôÏuðM÷néÌ̃½éúß}0=üæÙôÑÏu§Ægôœ*÷Ïu[¦úº'>pySo?Ò3ú—ù—Ó}͆1þ¸ßÿýߟþôOÿtúþ÷¿?ýùŸÿ¹aãžó¸„—>é-Ã_Fº¥¿ä³LO<“VŸ·4‘#ë<Ÿ¾LN}÷Mô^®ï \Óý%|3`SÛ¼jÈbëù9·¥Êì‹öPÖ¥Ÿ÷¤õö@õYz»\'&ß‚{kåe_—½ç俌¢OÏËðr8€ ?ò¡#Á¸èM:gG”ùJc]ðÀ‚íÎb0ûÇØ“ê6ƒ ÊØy”¾ÈÏ þè#.m‡L"ö ÔÃuœ–¯X4Fa…ѧ²"Äi^¡ºL*© ¨1 ­ÃWð@ÿ8ð7M‹nØKô˜é¦Wøá9Kn¼]Fè+—r›áUÂ%ЫÆ|!þ´±È¹e%ïñ—¯‚/ñ^9ކLE¹‡D¯No¾£6ûþôììîtÿþîôŽú‹Û{çÓ/tšîø±ú­CÝi¡·&Ûï|¤ùZ5Õ+j?ü„×l4ó×E^2øûZ¶ß~¢­H]ªóÉO›énüíéÑ¢“e`"SNÛcï× Øm­À=ЙªÑýÙŸý™£ø‡貤 ãŽüªðUiÁ¿ÊU:ðF?Vvë €V]uâçší`&oLδF¨7”T¾ÚP—A§kœmYý3‘ë5Ä Ñ Ëì†ntxtEØíoHS¿‚ñâ&À7îÞæàHë2 ¿MÇI¡ãß¾sK¯t逘–Ìq¼ÖÁRÿ»ïÈê&6×Mæ5­<Ò;ßtÞ¼úÅ5¯ñaƒý½ï}oú½ßû½éþà¦eÀðƒL?þñ=0`ßß³µ—%z°ÌKGÿþûïÛ0‹ý›¿ù½RxO^)ÔÒñM ØkæÇ‰rþ‰ÔÛ²:ì§AÞS©³2æjT{ZÝØçøë…¾/ÿð¾–ûµŒ¦÷×u:vGH­ÈmC¡r®¼3{×;¥šõŸi„9i;€ü2@àN„=>*O¼pç®@ö«PBå‰a´ÁÒÉdéȾú¹Ò(ïÿ»7ûô¨ |ëÛúÛº¿Þ»³§;!€ I „@Œz•EGï8:ƒWçg˜;z½÷ªã,2ƒˆ¸€32W½p}Ded‘‚$! „µÓéNïë·ï÷÷;ï{þßûýûÿ-Kçynuÿ¿ª·êÔ©zë­ªsêÔ©S—.ÞÙIO&B<îx3ßã™Õ;ÊÀtÕÎöŠ<ËŒIõó>`LxNÜ¿E 8ðëdf2NœþL·n¦I”÷ûmt2ws·‹¬«y«:ÃtŸuÓ7¯xr ‰kíá3qš¿wô©õ“!¨NoT ¡–e×õÕH”ÛÖ3¤ÖÙ¼N¼åú`X˜H0låëø–o\•RñaÌ’Ófä #ëÄ|Öo¸„ÕüÀg\ Lä\+O¦E%ªZø7¾KÖC³n‰c ¿Yf{8²6q®Pï5ŠX19ûXÄwâ¡õn¶]£ 2=á[~³Ž­Èo3°ˆ^tf;c3›ËȆ-Ì 2üÌQ(ä=ëY]å›z‡Ç™rù®Þ²­{™žØZ8Ô]vn:Y.l+C£¢—ƒ.ÒæC˜´†x³=>ÑéNÉœÖÎ+\˜/Å™†ÎpA c¶ª÷™ ¥š.V×¼æ5çȤsL¦Ë9Âçf¸ý9ÓÖò×›O¸¦[ ¯0Jw="®Á8´Õb΋mO=‰ÕL¸ïÅ|È™ŠoÃ\4Ä6á¸GÌ+©`ážçu ±i,úpQ>ý*ûYŒ-Rò¹šíë_ÁfÆoÁ?Ÿ ´hî bØcµ&¡ ÂãtÄþ¸™O-pïTîCëŸàÈø(¬ˆ»¸a©NÌIÝ } KÏ@ aüåÀåž{î)^xa¹òÊ+Ë÷}ß÷ÅäÿÐC…dàÉ'ŸŒ£Rq´;'øØ¯Çߎe8ÅÔ®Âø'O(‰©Î¯jŠv«' °fÑË Ñìð Š„îWk1*ñN o‰âÃoÛÊ î)臘Ç:a0|îÙy­G!Ý Ï$v‚ø3(4®bmŸ0F±È9T ñ<¨­¤'NC1;Ùˆj‚{¼(I¢4D®˜%r 람At¼:N"?DþÄç$/ÌɮЅ@#Oâ›iÖ³"ŒtÒ]Í‹3¿«å ë;òR10¬C^Ú8àL—a©ûG6 ›OÜÂKÀ}Åöæ±U-[¼jã|WÅó2Úx›ßú„#]\â ñ?é<±WÄn}}w¿{”o~ñXŽéÖEç$ásô#ðTÿ"©’€´žÂèbp Ç/¿‹±ßP«n)-#Z1@ÛßöÔx®Ë ¤¼ y#ÜÌ×Ê#nÛK?+dx5×€3ŸýI—aß=¿ñ†uÑ&ª`-·UfâL¿†{*^«|2µêÔ†/Þ3ËÍ´v? M¸|N¸|^·/q{Ì»)v²¥7ÇBh¸lÙÒ[ŽŸœEúɹy~”óΜÞþÐ@éïÙ£z¸õ“.z2L˯,F]xi–X[.:éÎ:GJ;{`<>3Îñt©nݪ?ÀTTPûâäÑñϺE«šáL×gî@m›ÅçÁOÀÕé!ßÉ ߉r‚}þ4å]½ŽNxŽ^Q‹Wº"ª‚8gÏÎt!.G¹çQmeÛ¾['±u…ÀG}”´¥\ŽRŸÌÀµ×^[NpôË-‚ã\ÿê*ÒN@NæâñY@“°2v „ÑW_ÀŸDHfÀ“•y\¬²~š[¡ëÑ¡Nœ8â­îÞ½ ÆYÞAq¸ ¥3êà*²} Þux˜2‘„¸b•°ŽiŠX y0[êÈ©{ ­·ØIÀû¸ipŽsfŠ[ì(gûŽHMo“ß­€YK(k„šú/ÑÔW2à)Á &"'S§!üFü‹:ñ~–i{I0ñÈÃ{“׸ È„Å+3+ðºlÓmŸ\ÕKÄs€c@9âG˜Ù„àšžùx°ãÄ÷PÄ«yꫯO"Dýë÷1,ü¬•¬ñê[/ëàí~ö½Èûø-²OšÏrãû€Ç6óÙ6ˆ»ÀcÝÜž ç¤Q—ã÷Ôù7ûŽø".|[“ü¦Gì’põ{ÖI•g\ºÄÕ|î”Çt`£¬Fž´3Ù‰÷=²‰²UVÄúù^+B×õŒòê:åw<×D°Ò»5aVŸÓÆ ü­:$~ü€oÀœƒvµ´s€ž6”AÆÌ"{ôó»Ëî‹`ü;q†1þüÖ£e°oS™f…Ù·ñ"Ž£Dª·£tÏrÌŽ£h3£§Ë&öðO3'NvŸ` Š-/ˆŠóÇÌ$Zø“'ã"ë=ÎQ:ìÑTLwÔeiÌw¨Ú9Q9MÈp»ßLslîsŸ+?öc?VÞñŽwļññ<ð:æ\ÐÌÓŽ/Ÿ›0Íp¦§È2~-5œæuNƒ"Us½Àf5ú᢭=Ž,aGÖYjÄm±ÿÀ„Íó bÃË1W aãs¶s@Ô?Æ嵿…Æ»D0:eÌÃ([€ô<¸óʰ‡ºÏ«]£ê†qúÙ<2LÀ»·•#GObP¦Ú‹íƒó5]3½Úd†öWs"•åÀhݘœ½VÂ.3 Á—;U„¿{÷î²gÏžØxÑ‹^Töïß[O<ñDÜ$g'øXų½ pÛî5Û‰ƒ´}ã´EïOƒ2½––ëe=.èQ2øT ÂNOR*°mû6«Ü¹ ☉‡¢§éLâ"¡éö -GÃEФ<%1Ô ¥@ï9@Žqv]ˉ3À{ñ‘DÜ•øì ¢l8Ö).¿P¡Rax%Ø:;c+Ÿ%Ö6jNˆÁ­áˆ´®™GEH×áu?­¾#8ä|•Ì0)„4?‰©øl¯ÐäÇw°UÌƒŽ° S¾qIh…Yûö—PØó]øFÖÙ÷Õ΃“ŸL‹.pÈ8â8qŸßкøŸ°¾‡á`‚€u?޵ΟʌÃw²¾-±à©òGû‘§å[nÄÖ÷Y—P‚gØü\´bE^ŵ¥ElæÉ´ òœ¿ç” D«näõݵãÉøF!_F×¹#>ð’/ÛP˜L_VNÖ]¿C9‰;üéÍz$þ¬Wûs W³L#›Ï¾;Ï™·•g=Æ­ZâCCÛ²áØ…ÎÒ@ÙÅ ¡)$—]÷ãÄÑ.ú, xg'9éÔÃS›8æÊ–!ªuô)ŽH=ñ%ªÐ]¶p/}ï Vóá‡eRæK,æ1ÜarÁϪT[&“ãˆîe'žoãkùÒe8ý•Ò^ßý·[uù½ßùÎwr<ð»bîuü'¾ô62ÕùšqM¸„锞pé¯f5ØjLK¬y¥!u«s2ãÞ¸èàö9>–ßÀ¹µ»›ÌjlÑÔaŸaéïËŒUÙÎø¶¸ã üHmûSÛR¾cç•`ò=à›Ø(Ù¶ÓYVº›a Ž¡uêJwîWs€É~’U­ÊZŠMÜÖwvœ…¸¥Ÿ[Ÿ&Ð`U'À‰9Å·úþ¼&V©€Jûöí+wß}w¹è¢‹ÊUW]Uþá?ü‡qj@‰€L‚ù°[YÕË( ØÊê^ÂR¯ÿí|3£„Éß ê¾£[¶lK- ãùÛÈv!¶ f!òAµÍç8*"3ÐMçRÌ´yóF¬d¡èˆa¢^ž5¬R¤Š~‹vBZkh ‹÷a»6¿ d ‚‰‘EóÞIý@hÁ{šÛ—Ã’G))òçP¤”‹íf;¥ã¡ _C‰©½_⨓ñP“>Ãñ­ˆ BÈspËHì]{ ò:‚p¦ë„÷'±Õ cX±º}`8-úýT³aIgWöâI¦ ‰|ŠþýÆYë®è_œQò‹+$ „NÆ€ô¨¾«{Ÿ³^æ3Mß|~÷dD„©_­ÕNæ4»«ƒtÁÐHBk PE\âo ¾”«F$’6 3ìO×*œ`>'¬Ï g¸ƒk••u¦³C–ŽQÖF\é T×Ð`õÎÔ¥U^³^Y^»9ùÓ|ŸŒëàgù­$ðåwŒrëçV²<34ÃÍçöøò*àx«® ‡ù¤ž[Y°lÞy9sÈbèå°„¡päÝ¥©qlÈCÄÇsÔwÃÖ²0p¨lØÊ%WÜX7ÊM£î4ìf øÍ[a gÏ2Þ´}ç–¨CQæAÂ3;É6UÅß:°¢B*³®å²MÚ}óeÜjáÄŸ°íþÞ½{Ë»Þõ®òë¿þë-üÝï~wyå+_ý Çk³ŒvYFúÒÛãÖûܩ܌kÇ¡dÏîç‘kmÐLNβŲ% ºy2­½¯9Œüh“d†Ø ߺZ°Ñ¥Tþc®èC9)Œ[Õ*ô·ÙÂMó…àZ}3#’X’O˜*S¶ÍôïPø¼2Ö6D¼oýBv'^ͮƞ1éž®û.šV¬.T€a ï{~~;·Aä6(E/Ø÷’H:aKšÎ‰\Bî/™Wö}‰üž={P¨yV¹ñÆ‹:1ñÓF`D1k:x©w&þËÊCYo ;C‡)ïèáC妛næ˜âÿ^þÓßgˇàÚƒX°—¤˜ß;Ž\ 3ª9g´£g†ý}º‡+~ ý8LÌÅŠÝÝ•ÁÎ;5ÃÖïßOü47R7Œ–ïÊlr¦Ú0’Q†Øz©©÷^ÏNbÞ‚m§ßÊM[îió'^E2™™f>ŸÍ§3Þ2÷½_[ln‘DÛŽ2I#“Yã™?ËdxU­H¬@`t«€ý»Ü‰G|éx¶Œe.ËÃo¥Õ0Ö=âj<­g´ãI¤mñ–VW5 ò¹W÷jeYÍrWÀ›Ñçømå7Ó³ìˆβpŸe‘ñ‰«~6©£3=apk±§Ýv1éoÅHN«nï¤@ЇÍ΂½²ràðÉ2{Ts’>ÃÄ4w}A)GÀTÚE…kpÃl99z?Çï¸ý¦ƒ€pW…GÅ ê (‡õ°ÐqÛszTâÂjR:@kÕ; ­oëk´Ââ_)¾FÕJO¸v¿‰Ã4è‡~¸<òÈ#­qn¼ãÑ:14ê—å´ãÉøöòÖûÜÄ× ?•üŽçYWÉùFô1F‘ÚÄÅqóyÎý«c5É6+3M´Õ†>ûyW@e p’>£Ô×=™î.·gkâß쯖C%í·]À<´¿cúw ò¼2¬ÞŽ1ù2·wõúlx/þãrÏ‚Õõø[«à^ˆÿ8JnÂlÜ‘§Q»Æ0¾ƒÉW>–Íèe?;§w1`çË<°ÚÈÎêϽ)EûnHôýy”ÐÕæ‹_üâ0í+C¡!ádd,$OÅ oºŘ'}8VË/dûA®ò(:ÓlWtCðÝÁ«€8ƒ”À²•v(P9Ò#¹k@ÆHFF?†=–âå}\OÜGû1§ÀMÐNÜr‡­H´a  b uÔQ k†2áÔ4 ñ£(vusw5ï‰,‚4:5tT-wsY{Ñ Æ šLèAt©’€D!;9qI˜m7ãtÑ`yjÂê$xM«ó¹à÷…FÚCßóûA˜Ák­ø‰_'Sàž~G²Nñæ‹W¿‹m ¾ød:,_¦  )ddtAè}‡O¾+@ñnÂòÒ­÷Îçð©MÔ§®TíÞõþ‰ö[†³ÎiÎw g™¸ˆm•_?GÊêÌ—í•h{}£.ânà°¨ÛëX×oõR—§f¬O³N­zÕeµêãs–™ëgÃËÜ&ŽÄñíg6Ño†ËŽíÞË1‚¤®Íû…2Îå5só(ç!ÍØ\NÂ(>ub¬ oEgò1ŒuùÝa¬í#‡ytrfQƘv%O_`ïx©½«ÌO§þŽ<{7—ÃUý¥ ûv«»|g¡2Üî'†Œo®––ðé7óuŠ3]©«shŽÁ&œsf2ÖŸ~'Ü™¶–ßÌÛ ¯•/Ó×ÊSé;)}e{†yW½+> „]FmúŒÃöG?¾f‚Ù„ñ“z¡˜s¹;ÿîÿlà2²)€«Bk¯šÚÇS´ô×þÌ|’ò¥Øï\è¼2´šÖ&™ˆ7…x•É|:Žù¹Ï 1ãŒë0 €ãœÑž<ËMp…±±§™CüÝó•8Œsp•úŽÝ—-há_ýõå·~ë·*‚´J[Ù¸)PW@B¯àÿøƒAPað¦›n*¯zÕ«ÊÎ;Cr c¡â ù” Ä„³J™¤N‚u÷=½¹îÊ={Êm/yIy÷ŸýY9 >WöîåUœ²†zèAéqŽ>Ú™fYÊ÷Àev³*ñXá—J„Æ:½Òm‘ Cw`‘•LІ†ÜÃöÚ_ûl`K ' +)Rr•Dñ¢p2 2½ÈµZèDV­ ½ neDeN°¡hÝwv å¶@tRã¨3Ñ‘?a$†Æ»Bvk amßÕŸqÉ H€}ŽS¬ÐÝ¿Ï-ßÇվϑ¿&ú¹Š7ŸùÅeýmkãÒeØ´˜H3•*Ç·q¢²Ýc;ÀzÕeÌ 8yˆ´Ø+´¾ü“)ð=C)ÐFÀeyQF£5ÚΞpuþhä_k¦™ÖÛòØÌÓÈ·`ùSõV§ñ>‰?Ú'Ÿ›Y›ø›á& áh—5â,¹'.Ë­q¦ßBQ§7Û%Âä‘ÁÝŠä‹•ùà&ˆ/[_ôþ9v†±>gúæÞyVìÓ'ʦžaô€´G²¹œÁÒäôìD9uú csò0¾sòc'ŽÓ•^Í`¹T¯› ŸJr”Ž~oÅ\6Qå¤ 5!¨z]&tô³ÝÓ(Ãé7ãI3­==ÓVòW‚oÆ·çU‚ªŽU{¼y<å)ŸùÌg|<¦=Oûs3O3Ü·Òs3O3Ü„o†… IlÝÇœâóÙôâXDž‡?ç•8~ìØÙ½W=c"°©ZÅó†4ŠŠj݈Æ=Õ¶‚æ€Òâ6ôµ "³|ìÁ(fŽ }ý#‚ÑV…«wWÏAÄÖÙh ‰‡ŠozÓ›B9P¯}íkåcûXùøò¼ç=¯¼ð…/Œc†ÆïC—à§d$«9ëâï’K. æÁA!qôìÿ×ÐG°îàö¬ÁáÁˆ¸Å1¥†/ÓߜڧL4Ø¢÷Æ=¬ˆ‰Î‰èЫrí-¶]’½§©²eÓ [ a“@[Ø“Á(DâÌ”v`Ó@k„ssãÁxyÖÕIË«,½ÕЋ•”Èœ8v’NMû2†¹„ÍzJƒèÑf¹×Þzw ïåwtâóö½¸ ƒo¦t#t Èý›`›ÄŠ_ç ©s‹@çñ»oYAü±Œ$öúIìÍ+3e æ¢N âìÀã+âÃéÓ—,Ç_µ S'QŸ-+ñ7•þÄÕt¶G hq™P§çãsDñ2ËrÖpËF«]à"ÜŠñ½MkÂ×° yÄ‘0N¿†4üiåk gþ˜¤iæ ×ÄŸqÊȤ•|ëÛ,#ë“ð­÷7.á3¬zˆ1RÝãq) ‚i¬Ò]\8/¿c#[i\I;5FÿG¿h¡w‚1"S¬$í &k/S„ûXÙ²cFp0 >} m{$L=‡* nVö=œñšCæ?:Õñò0'jk¦®Mºý¶Z6cWö³´ûæÈ¸Õ‰9aÓoæÉ¸ô›i+…6ý,'ýŒW±Ú9Ðñ–qé»-úSÍr2}½q ßî»ùÛñùìX§31÷y ó)é9¶dÑËàs–·™0/W¶b˜¡˜SX¹mãb-¤¦ô\»'?B‚jEÛ]Ž¡f¼qþêîÄÜãùÿóbÈjœW€òTŽTut— ŸÞ$W»Mc–Râ>å,ÄÊsï3óè@Ï Ô¶i¤ñLuDp‘;"5 ¼wí²óye¿Ùg2œ¾¹ÖNì ›~3Æ¥ßL[)œ°ég9é7ã3Üô=ú§RµóX3Þü®ü_ÿú×ÇÂêÎ;ïL”çÀe>2¼’¿˜•ò¶Ç'®X,ð­Ý²ØÌåF‡8BÜè+•„Ñz±Hd…ÄÌà ÃS6Ò‡µ±@Œwã;_ØYX°²Ä!p¡S÷òìß\;{Ѳ1ÐH`îQy`m-ÏÌómú盘eÂæüŠ U9?+T'öìAÁ±‡†<­4;=ÆyØžXɪãÝ¡‘ˇãZ`5oÕv÷(ß3Ÿù̧Ìä±A®|ó›ßŒ•¨¶Üÿ—X{Š@Q—\­Ç^´- ÕA·„“0-m ˜''N‰¿Ê02¾£ï'³°‘Õ»/ñʃûŽÕñfN¨0¸y‹šÅˆë950Ëž½Ç ţؑÝúÛ[§!úh¦RîXM˜U¦TÙpÓ¦!ÒÐ%àF»yžwíáˆ%' äêx‘gá£X*œ¥ÃÆEp¼ãœ®ÐÆð—ëx¹·\qêâ ßfH‰Ê0›ØžI1¸„”Ê‘ZPnFØGWû¾«V%ªÕ>?oΠðýÙ^A ë‘ù‰<>“DY8Ú3VõàÑœ¯íœ;`2#Á$\ÝÉD}(''ƒº¨ÐHæ@ßr­³Î- Þ‚¶¬VùÑö”×AÍÏpü·ñádתwôŽÎ@ççVj+CfÍQg³.ÛW'·¼Ä—ù²oÚËòó.ñ¥od3Üé92®ü'ëšuÐo•]—kÜ0vêGzž‰%ÎÅ2ÄÊkvûõŒ¶ù6ïvVžœ(Ï¿b¸lØ~q9{ø¡rlÏ|yè«w—'޲6¼X?þÍrüà ûô\]»­‡q{š•Üx܈-|˜ú®^lm`ö¶«K½ÍBsï†}™=þ~÷{q¨Ღ†×vÍ>•áv?±d¼ÏÍp{z3-Ã+ùp­›eµç[)O{¼D_£?þç @;÷Ðu¿ôK¿TÞö¶·•Ï~ö³­wmÇÕþÜÄ•iâLËô•üÌgº[ÖËEâk^óšPôV"›î/ÞóžòðùpºšŸƒØ»•ÊÊ_;ÿLÁ0†Ì»,¶”¹.bס‡ùpžíÙDÿÁ(° ˜åÒºEîÐ-.Ö×Ô×}¼Å3žr\fùQ°±üg2cm­©ñ œo€Z8àGÉɨn¬cò]d…¼1Öø$âcZ¾—ÁÙ þcB—óâ4|e»F âÕÅD­BÊÞ½{£S$Þõ´˜°ûßy¿úÕ¯ÆjÔU½Ö­$ævv€´- Òà×¹#@˜F†naå®ÝëÇ{,¶´-`~ „+|qvBÚ|×të!!6oÚ8vØ;†ËÖí»âHà ZýóÊš$¦¡}ºˆÒGVUx_í÷$€{#Û¶q7¸‹UÏJLm‰KŠè¨®¨µ/0ϱ%ÅüÚ+CyÐjiŒHNØYW¦@NHD©ÕÜÑä|2=UÛ ¾Ïü<+'ä£^à$¡Ïö—nH˜• $Q•€û½Û@^óÚæ ¿ñÆÉ((UpËÃJª¨‹<íW¯á#Ÿé–IÆ€1¿¿(·Ž³müY†.¾i2D±eQÃA8¤À¶ðWg|ÆU9¸—Þ²FÕªOÀµýG¸o&g;U¥5ÚMø6ØÌÓô[ù€Ïw6=ðf™‘%5sŸ*qF¶¶çssT1Ùú›ÙáÛ¾õÒ²•5ÀLiWïóÊ0³kö+fx^ |óå›ÊĆ]¥Õ±¡Ñ²ëâg”‘ ŒæŒl+lç— /½±ô#U{|`²Œy;Hª°ØÙíŠëlŸµ_ÂÐÇ'Ï ÚETۥ쾷>)„ž‹ß™é–_óªúvúkûéÒo†›qÍøö°Ïº„o÷×›HÖÀ“0MœÍp{ÙíÏ+Áþ—¦é4¸–óX3oH袔׽îu|‡ùòentH8“;…3î[ñULÔ:¡Ün¸á†r饗†yøº*Ë<nJ0œŸ÷±­{×½÷BS¸"úWÿÉ[>g°@Ûçqt™‹q©OE*ñs'±4Ê4Ñ@¿ŒWÞ 8‡tº5¦ë’}§ö¸V¥b<óD£½Üÿ?/V-ÿ¼3¬ð Ûõäëªpã !p‹ØÃ›ð¶8ö…!ôî½ôÐà‹\½¸ÈYw;ÜÛj°÷"ævÏæ,wU{¼ÏÕ¹ûÁkíÍûÒMçöƒ«t•ö\¥ÊL(¦·,EC‰¦â :i[@8óz’à‡~臇JƒJòèaâ·råj= ›fn:WõþNÓIU„ÓЫn÷¢ú"·€mâ$"~XDz "ìu”lÄÓ~ÕÖÉËšQ®-ñÆ0öôÙZ™›Ó¼-Œ‡“ q^A<ŒäaôÌÙjk¦`#÷œE2 ‰K·f$£¡ Èd NçÃ)ÊÕøÑm8Ê­†J+|Eh4 Ï*VÄÇwó›«#àTê{{ц½¾½UYFÆ[ýl“XñÆ‚ÌÃ$¢“±‡Œ€ïä·Š&¬Ï¤[nhóH¼Ló·l‚áE\ýG}ì—¤ëìƒÖˆÈkoü[TËÛ<¦ážàd4H3GRKE6í®ÆÙŠ_¸ºlÃYÏÌÝzFø6‹YɼÀG=;”éküIÜu-[Ð>gZFF={!ì ›Ê3vn)“½;ÊWÆí¹÷l×>pY¹ñšÙrïé=eáØƒå² ÎÒ¯FÊI”ðÎ>9е¼R>|÷‘òÌï*ÿÓ÷]“¹¡ÜpÍUAªQ .Ÿùê×ËÈ¶ÍØ y°ìçÎùyŽØ=þCÁ8ð–¬úé«ÀÙ'˜>pô£|oWqM¸NáfœhšÏÍp‘qí~3ïjiëÁ“0MœÍp;þ|~ª0^ºv×]w•Ûn»-ˆy޹v<ΟM÷¯ÿõ¿[JJ³ìoÕϲ”Ðzg˯üʯ4‹:'ü÷ÿ÷1w»¸“ØËœ8—'}û« «\Й•- óä,…ð“:3²8c7•÷f~a®(Ý\^Æ‘ê.Ukø‡9¥V8èéo\\¬Ê‰Â(Ãq²¢‹þYA0i`Uðñ| çà˜ÖQÏœÈxpoYñ‰7þ]ˆÉ܃ˆïäæ½|Ǖ維!L+Âa9ïv÷rE.¢ìIŽklÛ¾5&o¹?Wîípµ6‘+µCØ™Ð]õ» ×X„Û4;OLž Þt ¹¾JæC½Wú{÷î {ØÏþóCrÐäJ%ü:ëiyéÄéO ÀJN%7GÑžp{@kŠ[Ø"°/öqù J¡?ÁÑA• Õì; #¥"ß 2Žüa Dap+AOqŠbkb¬à‡$àtìñÑ3±ÊÞçÐí ô¼¥o Í̤Öß2†PžÒı÷]«d8NxçP˜šDaÓã†ýê @Üg!~¾—Þ6ô×Ú?·Íéæ’K‰¦ÎtO¨Q«4ˆø. $ÛÇo¥¤@ÅÁ ö6Îr|–QÈo–忤ã³a ¾ßÁ_ê,8LIŽüGL'ƒ`Z<“7Ê!Ÿ8ÒE^ôuñ×?ÀRgØè„Átý\Ö9ß© /]ÓoÁ›åfXÿ)º¬k–avî¼ä› ”C(r;Ý5;§ËñË‚Áìç’ªS\;»s° M@±w¨?…¤† r÷¶ƒåoï@aoËLšcåäþÝåà±o”#sÛËÞË=o+Wb2·‡ûPNžœ+Gž|¢Ü ›¢/yî5å_ÿZ¹lÛÖò‚ëžS>øØé ùBÍfܹ~öS2œ~3®=¼ÖsâH¿ Ÿqé7Ó ë2­Ý¯R—þfújy¾U˜•p¾úÕ¯.þtü Iáörì{?ÿó?_ÞúÖ·¬¼´íg~æg‚7ç>ÓÌŸ8ô%î9'º5ûÏÿù?/ñ -õ°Vrüàc XK¯.ŕ㥉ßò›ÏÎ%ÕØfNf5ÏrˆtÆ5sž—(©0Æi(‡‘Š2}(XϹèrߟ…ê4ÁÉIò’gÑímaQ~à®jœõɱdlŒÓÚÏÌ\t°Êq~þžw€UöþXý×È•¥GÓ\íîØ¾™ýtî f…ê-®(çGa€öðšJmZÊ›)[¶ï‚"Qu¿wïÞ§Äøñåüä Ý'Ò¹r—ø»úö³Ÿqíüp)^±“Œ‡—Ý‹8)/%R/AfÂc…ÖQ†@)…DQ}DÄòíøëq¼§¿#lGŒPÇ#›ËÅ—\ÌöR ´µ$¨ÂÞÍ+è8¹-‚.Êñâ!¥ÉI¬ ?MŸ€vtRwßÖ%îvÚ>Ä\ƒƒL¸dŽÁm¢ÌYöÉ\À«k0ÅP 1ŒR¡¾L›Ø¶Ãœ¹¥a[:XŒ Ö'FS%Þ·ŒÜpT’¡j…m|‹Ø×õŽDx^8‰³.¹ iæ ¼àÎIÀ88u›–°ú2BDd)ÖÖv©óQ9¥ ¦g\ Go|U§Œ\ùPãÌÇ€_í Å»e„u7,Lã=29’ò!ñXFÎ÷LOø~–¿*ÅÌLc÷–Ì#(«3$ìÊ`ó€3ÕXÒÒ,ªpÓF¾Ú²šÉdx„îA?Ò‡n$:¡) ŒíkÙ”AÑE›»’çŸw „MÒÜH¢R€ÞÂE9Ö/€‰kÒ¨sÏd×5X†b S_û<5ñT‰ ²ãäÑ)o~ÙbB–ÕZGرÖݵ‹ü(`¢s³mÛA»ÊŽ]ذÛUž})“Úâér|îù¥¿ë›åèìî²yÃ>¬^îÂä-ýkà‰°[±mæžrˆ£wCÃ÷`wW9=y™ã‚«òHŒÝGíc J8rÇþêé1'ÔÓ¤sgE®âùül¿ÂpΖ׾öµë¨½Í,†%¿δX&ÓVÊ—8Ò_ .ñ%|»Ÿéégz'¼ëIëÓÄÕ 'ìZ~æQéùñ} ÷†7¼!ÆÏjù—ŸúÔ§BÐPì·¼å-AüEâüçϱ—®Iüß÷¾÷…¢¡s¥G²-ËEÓÁƒ[c'Ë·¬tÆe|úŽuçö«¯~V_ofž]‚ß¿ÿ,ø¿ôƒ%s€ú ©Åa¾ôúyä«Ì£(²èœãì?=“-S:,RµVKÏeçDZˆ3YTðoúä¿tÍñnOm¥X_çIPy ù“™ï|øç@¼s’¯è²³?%^°°m û€¬1;9GÚ¼cë–AÄÑ쫸÷ #Ð(zA›»‹Xµ1©(2rïý/xÁSj3; +q;¨{Jйũ†¨ }ÚÐm`© ´Õ ûŸ z&öoÿöoƒ1Ð^R…×¼æ5qz@…Â}(¡X;m³³¬VV¦Y_˜!òÀTÜ;×F¶¶±%Ѷÿ8JQ#ÜO ñ$•[â âñ±Pü›g•ý–¶uoÞž¨è^B·r*cQ¿û`›P8ô¢“ñ]¼&SåÃi¶&”(œ9ËÙkˆúÆÍµŸíñLí "IðTA?Šˆ¾›D~ŠmƒiÚ}˜-ý¡‡ -;¦sãÇã¹jßÕá¢r(È–}$äfÈ6´=ÍãO'“¡óYFÃtñ‡TDØdð‡å˜ËvÒßüUß̾I¢€M`3à—I†›ß9ÓÐÊ.}óð3¯.ÃËòUIëøë¶ÆæÒ;ˆÕÉ^ÌDO]P6)Ã0oGï`‚Æ–ÄY,ÚÁ$îÜÜÃØ»èýe]ÇŽ³å³ý#|¡KËC÷•­ÕYìßuNèžeîØæòÄ|šýÏ3Çè ]gè³0ëÕ‘fÖ…0¼E+,0&ŸWpM&mØ‚K`›6]ó¹N˜ŒKßø §¿VÜJ¸šù¦‰«nÂfx-¿™¥ðSÁ‘°M\Æ9'ië?+q¿K¯ä;Ÿ)¶7]%jÝ/ÿò/Ç‚Ç4]3o†¿}ûöÛo÷±—aa¾ÝwáöÌgjÞÝyöòÈïùŒ¯|ùHyó›þ¢¼ï½w•'¾Î6!šüȯÊðΓåÂ+¸^ÙÒ)H“JÍ̧17ßË¢§K©ÌfzbAÀÜèÜ1-1 +ò·Î…=ܱ2¯Â4F¢‚/Pß´ÃÂäx5gÓù¬~ª[zÈ…jUƒ5¾ƒáóÎ~òɱKö\>…źþl ÷l''0µ‰æ©ÓwÚ°0À~õ4Dj‚¯}|÷¦ãÒ &(/Ëñ¸ ¢øó/5{­v“hH°]½+ŽÒÙ‰œTÜã—K•ó܈qœ^V÷§9wgå×@¼2vx %2ŠÁ$üê+¼â¯D]÷®Ü:°.¾S“;^­8ṙϺ[öYʤ¾Ça f蜳®ÆÙS׈Ð,m×Ûƒ’ +, MLÐÁMcài{ÁUÿFÄúÓˆjµ¨™àiNL;Gãò 4¬¥¥r¬É£L[J@f)o†òÔõ[…ñ"Ús–ï9‰&÷6¼ٱ—o-ã@”í;J€% ¡ôWË¥BãPñ}yG“E¶SNº<Â'¬íeP_õÜrŠÕº¸x Ǩ/¶HŸùB€Q'ðs[n„ÍázíþÇÙ©#5–ïÈS§µƒFe2_›/¬ø…9'_ÖËH´RÖÓk[y2ÃcŸƒ¥ ­úG8>‹¥IlÑ{ÍL÷(÷È?‚Þö5Ç1s;€"èäŽ2Î=Gü:“Þ…e’Õÿ`ÿbÛÁ2Ü÷`´ßLÿýåìIÎE£í=]=ÇW̼^å¢ÒQëF WÚö:¿g'ç´/Á¢¦Î~߄˼Í|—¾iN­¸Ä—ðí~¦§ŸékáM¸v­|Òÿ¿Äáüçâç§ú§ã•Þÿþ÷Ç©'L˲šõh†3½é;Ÿúì÷Òo¦™W׌k†;¥¹°rEÿ¼çÝÌ©,Ž†ÔŽi°|ü£ûËÛÿø”}àKåð£nµrScÆmÖËù1–dLY¼°5EÇeÑÆjŸzAmâÔYØ\ÁÊßüs%D=žH2=2н6òk™Õ;<"ÔÉ´pžTR?*œ}¹ž'r¬6vYð°¶`ìü¹óÎðjãLê§ð•íÇ›Úðk¸†i3·¡¸v [õ[9744´[÷gË»Ð`Ÿ­ÑÜRöúÙí[¶G§rìE>Y ïzœÄAX;¥7ž‚pJðÿ›¶N&Î^ÜnìcCh] €eKøíôâòü©a™ ÷¾d¾ð…/Ä>Ôž={b«À³µŠªdÔ˜•)p‹@ E“ø´¿—õôÝÝzûl¼m]O'ü‡ÿøKyô˜ƒMe \î&Vèýƒ¬¢áh™?ã. \4Üu%B·[&8ï/.ú]z{=]Ájnj <Ãe¦L†L]„iL¬ö°€2Œ§†1F¤öõÙ³“SÄÞ:G±†4Õ;ÍIŽw±×¦Ä'´kÁ«¨Wæ#ÞÑ£“ÈÙ7|æ'AW$œÇ·âR"À´9 \ \Þ»)1ˆp’ÊÀ'ªŠ·<úX™HfB§ …Æ··µ¶½­J¸:ÝþŒG$Xgø_ûªÞ¦Õ[8ë÷‰:‹Ðtãj¸(£þCJ+>ëF—Î…ïfÅ~ (í,†ÆÊŽ­3å±ÃHưy{åî3å‘GÊ—sã$_/"øÙñãåÄÔÙ2Ò ãÌít]ÝÇ™ØaÂù®[ºQ~í?Z¦Ïpò†zmê¿ F†o6‚dgÊ›'™þ¦8^·¡ÇM ƒ1*\=­ç¯ï¤K¿=,¡Ñ¾F£5ýgögƒÑO@¦?ó·ûM|«¥5á ëÚáó¹J]úÛŒÏpú«áY ¦™¯Î<é¯7m5¸Äeö[;ש¬wÍ5ט-ÜïüÎïÄ|äƒpºÌ÷íúëÁ%ó¯.Õ 7Ü ÂqPN3¥ûÊ—ÇËûß÷µò¹Ûï*Ÿùä}ØvÐÒ€ý†sŽãsëLn°#13#ñ—ùçH Ð %Ù©dõÃÀÅO-WÏ9ÙVͱۚj`"ÓÌAõK´0|GO0Å‹zÖqsFL4¾—ØL#¢Þ¼q0nýÃ~g¯Ñ,ã+Ξ¬ô…ð –¡ÙÇ!N›8%0Ž%; ¥×*áZ/àHÅ·®¸òÊPès..Ï™+U¸æ²måU/{^yÇ_~º>©ãuLvWå£~¯:6ìs{@_›*º¯åO „DE»i[àþûï­óÈÈ@´;ßÁ4Û VàT•_…)cm¼‰;|Ÿ1¬ùëê>#° sÊÕIåyöôÕråK¨Œ™D50û÷ˆÂÎÂ%÷c=/Eg–¡qUß&• µ 0@Øü›†û°Î†ùT¬fmߎu¤ O9I¼¶fK߈„DÑ»DãO|3/?Ò%`ù1Ñ8pÖU¼EWÄÛ<®â­‹ÃOØ\ñÇàŽ†­S~*q¥WaëxËçÄDh›†¤¿…€Ìⲿ&ñ·„~`³ŽYN˧œ–®õPL'>ñ0ÖpL„‹°µ‹<$lÜÕË)‘Ó((aµìà¡2ܵ»ŒÃtMÌòíFʃûѺGÃy~þqn¢ÔR$’‚A¤ˆèãjm íôsýèä)¦A¦B¤#–>;í‘R&HÊCû¦jû,|~N|é›%Ãé'šæ³Ê²2²Þñ¡Â–âèGy$AËwÜ«Q…t™?}ãŸJ¸ ›8WÂÑ„Íp»ßÌ»ZZ'¸NqOG¯„KɤýéÐj¶£ðÿí¿ý·8+ßÌ›0‰w-¿™·^)Ÿ«úk®¹–•ýmå2æÜ¦ûô§ö•ß|Ï_²º¿«‡E{ ºmç€ â̳z 1ލ{‹Q¨aâl>a –¸âœ>>B‘/'›ˆ&µÊ)?™ºj"ã·®¬õ±.á–9¨N!TD2BÓ[®Ž~žÏÖ«Ç;6—0þt™^U€Ô¨È³QAU#Òé3§Ê¶¡SåÔ4,ì5NÌÂ>Ãt9~t‹¥«HÎÔ÷õÐWfñ½ÐóÛ )ʃ¡ƒøë*9î¥åsg¿ÕÆ$w 7ãÄÐ|n†»qŽ? ÎÇ?þñˆ6.Cž~õW5ƸJ‚×ijV¸™žeê7ã;…3®Ýoæí”Ö)½S\§¼× ¾S\¯滵ù ¿ð 1‰#«}•ötæ·mWÓpM¿^)¯x½wåúëo)/}é ùÎæªÜq–†ÿèhùä'?X¾ø…ûË“8íâçŠ)ly ?—èÆK¾í;‚Œãœ´º%ÐPÏæÓáù9BjŸ~^¦e,ÄãOI 4—±^°“‚þ4ÿâj_ó,2ß"5CR9Ë €‚ôY €Lµœªªæ¨~17LsÚʲd¢n<ÖÎvhé¥H^>…Ϩ’ñÿß3¾ìÁ¥V5Á¹‚Uc]Íp ¦ÅŽ2‡èe4Œü*¢ØfGÎil90nЃ ºb‡«O¬×Ft.Cáe?P]ñIh%¤WÜvC ë{æ?*›7mƒP€±ºóƒK¬%ô®^”JÈP(Úweî»e)qP`ºÊƒ}ßE-\¥ÚHK„êgýüeþdT4´<ñlÇðvrîs{Ãâv˜„ÏÝþYVŠ#¡GÐO}û0ÿëÑÌ^n\ä(Œ"²í\&4Šýt• UìF*0‰Hßï¡raœâ@ä?võ5dÔ Xä©%ËÐl=x–Ý ôºÑç@ÈAB8Eò±*;8l/‡‹í*1V€,ôb‰K¥> ¹ùö+çHTÇÌãÍ}X‘œƒØ“óŒ.“ó ÌEµÍœ'Xì0ý1OB«œÀÕrõ¼â¸^6UÔsÒÙæçóì =oNjzÞGöÛ :;Ž 3ŒÆù4ÚÞsïÏу+;.©A¬é²wwŸ<¹EˆLÜ» S Èå(Æxœ$ÜÍ=Ã(`?' ¤=” ¾ùÍoqüe—]VöìÙDÔÕºb¥ÿç÷~±¼öþEùÑWÝZÞúŽ´ÌjèϰׯèÛÉL±¿«t¶È¸·e9Z tïÀ“hËÄønM¸*SpÏ=÷„‚ {úßû½ßL…âPMZʸ*—( #úWüoÕ%påÀ·m’hµ×7·N&ÑÌŸ˜ç<7Ú¿H*†1(4‚vxbþMlË  Hv s«ŠÎÕþïeÐ  ùŠbŒ+ÈQ  oâø$«NI§ ‹c‘÷žgƒL&î Çi ]Ø @'€sÞ2‚9‰gÝ‚¸Õ»„Þg%I°£Ïð¾dlõŸŒ‹‰x]0”Ñ|Ķ÷…Ò!8ruŸy묭olùÙ†1Á¾ƒ]gâ(¬òk9©V@õ_átúÀü(›è¨[ ‘u9õs´O†ð.Ì9z|¶\xùÓÜyß{Ý•%Ye]Ž'Ñ·ûY—ôM_O¸.ñvÊ»VÜË^ö²¶{'XûƒÄ¥==Ël¯K'—~3OÆ¥ßL[)œ°é7á2.ýfZ3œéíþz`ï.*4ƒë ÛÅ‹Ê|JÓ¹°xûÛß‹“f†›ÏÍòšáÕ`œkn¾õ¶rÝso+Xun9¦³òå¿/åÿ|;VßUÊ'ÓÏ%ØÙŸAx/?µKÃWIïq|ø$úÎ’*icÒGí¶ÈPHè¯ã'>%uÁ#sÐ Ä0¥Öürç¯DBxms'ãön:8L4 ƒz¦WÊÒ.@ÕwêE¿‰EÛÒ–³è}ÒÝ\¾é"l£GÀ˜F!¶“Ë7='ͱM6›#@G Éœ7çwÇà=…Òér2ÇhVè¦Q6ÛMêaź\€89&J‚3(–¡­×6€ÒÞ‹0Û1äF®®ý»d'í8ùvxS ®ð /®˜:$¶'!À$ÅE÷ÞÿXÙwÇågò•åþì£QvtË¢´´ç$%ÁWCß°u”Øþ‘ù‘òGôGÁ8‘%‘ñ]üÉ8x%ê2ýæ¥D*ëˆO±uö} Ã2þœÄ×ÉåV‚u’0_:I»Ê læáà0çüas½.x«³lל9£€É²#1a˜úƱ>4Ï•Í³Ï B8im³w¡Û¹e=k.Ã'3Aå¢Lˆã ]„ù.J)’ ð̬Î:Æ7K¦€ç$ô™n¹BÛ¦'Nóƒ Šdʆ&Ez ä‹v2/añ¸õŠ*9úéÄO8pÕqæÕ5ã#G/ãÎ8Ö£ÍY×t‰ÿìY®pæ[?M?èâÈ'Ls X¶ÝO<雞áô3ÏZÏ+åÍ|é7áV ÛŸ%VMßÄ!í˜Ê¸v¿‰û[Mkâh†_§¸LK_]>·û«¥µÃ:Ž“‘w^R”ïÂÂ9@«yê5}Öq­Ýû¿üË¿ ¢oœxOüÍ:4ÙÞî[Ë/¿¬üè+^PvìyYÜ{¯œëñO>XÊßü‡R>ñõì;¹ :DJ÷N3¾8yçRžŒgF0¾‹0ýçòsNø1ü‡˜`&¸9µL³•€4"Mh5‹ÊnTeeßׇè’ÏLCO99ƒ&ÿ"w®ÄqZèÒ,â} ªuAƒ*X È?† E¶hªøqÁJôœ7Òé2gÛFZÌ UÉýË€ÎÃÃÓ°·ü$“胷7axh"d#s í0ûĈŒ= ãf>ö‹Õ÷&ÀYVü½ý›ÊÑÃÇØ“‚q&3N‚Im\?cýLNÈ+4¦+~WÙN8_ó*~7N†àW\9?]Þû7Ÿ/o„xÁõW•~ÜíšÕûÿz¥«Ÿ%ʹív…§ öíÛA"ÜdšØ%ÎâQ¯ÀºZG•Í+Wâ{å+_ƒÖ­ud|Ën'ðâ—˜ùgî 6ËͰÄP˃þ´08 S¦ý…ÅEÄÍhÕn‰/ˆ…Ól¸7¶€G/Üóð P^±©ŽÀ[ sÜTØß¿±"Э´DXý¦Nôº$ÖC†!Ä÷26äó_0âÀU+<ö¯Ö`‹TikG ‚Ÿ&„k2M‰ &|Û']”ÏC€àgYÖ#ñµ` dëúgÉæÍü1x~*쉧Å¥-(\äâ§3gQlRBá{ñÓÙM×|n†;Á4Ó3œ¾ðN¿—ø:¥5ã.}ëíÊ6ëßÄi¾”h%AK\é·Ã'ÞNé`;ÁuŠ[-o'øŒk÷ñŽåðþA\;žõNß»I}ã ¿ô¥/-Þæ– @¦§Ÿ ÀŸüÉŸÄÙõü6‰7ñäsæ[ÉoÂ'L3®qkùM<VGè{8ä;;ï¤óžûü}÷Ý[€í¸³m” f8aw'ß1ûìgW¹ú»^PžwÃUY\øÀS|à½(æýi)w|ª²÷¼²û¿°ú/K¹‘yE—e«^ɼ{Q9ýKëÿN¿t‘¾.'Wò‰”´<§Î!¡Gz{¶1ÏßW§¥w˜ôûy¸ŸÂa$æ8Ž7—óˆs7¾ñ›ø)5ÐÄ8–þ6#@ZÉ„Ÿ1¤.ãºýqA0¬F³õófN:%›†{Yøh3 $œ]pH9µ.¾cSÿœ9¡ókñ1·pÄù`æ=_~£ºç«Hvw8}Ñe—úÅ7ÆôT7ÐÖÎõO̰²­ñí[¼dçtØ–W£|˃0I423ñ àÐ ƒF‚6²S³ˆœÃÄã ¯·ôƒ™vá6x”¹1”@¦a ¦ËÛþË{Êë~áŸRŽ Ã’¸¾z‰¼æ0¯½öÚPØNîjÝU¹â=¥‹H<6¢l÷Tœï™' œ}}m¸zRøÃ?üÃq…ç-·ÜGÛm øŽþ”P|+.™‘ãG„DÀ•¾7žÁ®ƒwkÛŽ¶½ôyœ÷æspò€{øÎýl$Á³É0bUQœÐ‚°žÊONx‰ßôd9àŒ3,n„<) æt†ýiK@b hkÀf~ë"Ž~ó7;T>Ôïšø[êw¤e½ŒO'¬ø[èW¬ág¾ŽË.`ÄgŽöÑ—9„yh•žÛÚ±*.J Ì«¥gZ{žöç„K?·ýi¦e¸ÝO¼2¯®FehýÎípù,¼{àÿù?ÿç"# BlJÐ&ýÄÝÉ7N—°éwŠË´v_Xë‹ƥαï¾Mš »ÄÞ­»õ¯þUèêøœN]ž~ô£¡û`_k–‘«ùŒ3O†Wó•^ý å–[^ÆvãÒüâNã]_.åWþM)ŸúÈÌI:L›ëb1Þmm:ý¬íÏàa©ÿ,ßn:;û%ò|u9Dç'Åüþ8®Zá'ÑßÎ=Pü;Šÿ€QW :Úçž¿aˆx[«3øù È#¡gµJ‚L<@)=è æÅ–”\É¥[dշꃮÌkâý¥žÞ!¼l7' Xxötc`«ªnkJÌÁÃ==Ëé3¤žTëŠ|Jêpu¸Ã˜ÎqKEÂñ½Q£š<œÏçËZ^nš gŽõö¨;»û)Šó%bƒhšÃül7D~E3Ô¨yø$·F±÷/3W×BpÆ8“î`Ó¾¼Îóö*£}Ï‹¯+wÞý"ѳÜR&7w®spº à =íV %q6m”=ìmÜr¦ {å>~GyÝ¿ýÕ° ¸ ¼Â1$î¥%N•ôd$Ò®ÒÝþxÏî¤ñT\N®ð\”"81ºrPÏàÃþp(3Êlx¯·V=} ’•Rëe=¿g4zäïØáCXä¦B$Û¸»}qZhÒ¢ 8‹ ^î ¡cˆ·Æ‘WçÙz‡jN/âl¥ó=eT dVL ~N~Á ðlšù‚™¬"$Žo°–åsD×ÌA”IØòëüxqé2ÞpÄ´*i ®Æ›ŠŠùN-°o¼ô·Â^?×e-¥.…N|†o>80Y.¿dŠ–¬¥&ûÊæíLh0\M*¶l¯f¸SœéºNi͸ jéo3-Ãí~o¦eœõõ4Nº$|™žñ™O_Kq¿ñ¿IJÂ~í×~- ³RÞf|¶û«Á8ž¼“^±¼óOÓ‚^T`?Ž;ó~àÉœóžåû³Òe|Îp»o;íÙ³§¼øÅ/‰cÃ<¶ñü͇FËGÿæŽòùO<Îö¡½Æ£v×á+á¼´Û躵3 é>@»¡ÑÕ2AbÝI½ïYÕØ µ-ùÙO·c[ý¹«|€}üÄ-qŸ•5XÕg©ô<·Ágk b †I…Ø/R³ùˉ·†J3gù=Æý¥`ÄÉàР:³gJï€Ì£‰æÐô9‰ðHæÏ²Ý¬Áç Ð. †gÀ‹ç$/§ãfUY†PœàÛ 'Ü*cÙ4¿_Ìo~ ªÀ|3‹TóùußÞ¬ÿ­×ÕÖæòÏx÷ª1˜ M¹Jë…0x„l ûÿZü[àX™ûÊ} ²mXÿ›GÑBC2Šö½án ³Œ!G Ãf}ÜÚþé{J9þŲûêÅ–½û\'±U$.N'áwe«˜{a†³½„¿ßxø þΕgì¹°GžìEqp'«}®FO í9fï èçzX ­“?ëÑ #b~ xaG .öüëIQØ<Àéûíc»€çpÄE¨áÇäY—ysÈÆŸœ<ë[§ì§Ž`K*T"kä\ Ž€­ã„ó]ÚáWŠ_BåüuhDµ‚¾‹x/*åÐq®ÜÅ<ïd7¦›ašûQ^Z‹€&¢f[d8ý„iúÍ´ §/\†ÛýNi÷S?õSq?†Ïžx1ïjù½ÖKfšNÅ@‰±ý?óšžáô;ÅeZ»oz†^mzÇÊx¯~õ«c¡!žæu¹>+ºW‚¡µBü›nº)nµ“9QB©.OÓeyöë ¯ä›Oéæu×=·¼ðæÛÊž½Pè†{ðùò¶ÿú¹ò×|´|íNépO}EDß$ú·â+_§ƒþÎ=Èø@ÅÜ(¿ xíù%¬˜ùÛ =ž|?]½ó”Ûž±~vuïž™$öR:uQâ 7Ãqcô¯ªsµúÑA¢Ló|œŸ·÷ÝC ~ž$èR•^çSH݌ˬm@½`ÉnW÷Gx¶¨?Òd8<³}Éžæÿß0ùn0ùëA*°€³›J0§§8î<#£Í½‹,p$m8û.c3Ç®~Ë‘ã–t1›Ýsõú”Z¬…ïÛ<] €«î'!øÑHõ$vzE¸(eõï sîùw³‰¼À~ñ$DÙ3èˆ~ìË/òa¶mA€±ÌÑ:è5{—êlô£ò‘w|où™úŠòÿõÏ«Fok,qI앤DÚ¡Dqû{æÕªÜ rÊäå&ÿöŽû:âLËt@*ˆÆÇ³#(ž7n×î–&Ñ)$¾þhù'¯º­üÞ½?ÄÖ‰«Ý—P9à¯;Iøá%¨ÖQÂoØÁëP*L÷ó?ýŠò/~ùa˜šfËdjgßIÞ÷på##ã$ç„'ÁW¬èIŸýY¥ŠÿDe\Õ(=Ðê ÏßøÆ7¾€aó&áí\‹µc'ÆÎbc€²QT9ñVG©{ÛHç`âo˜~¢kú:ÎgbäÅo1 ö«ÑŽÜõ€ üu–ŸÎ@­Cf™8,×p«^6²ª§iÖ¡éâiY‚Û€µNíqâÈršø"ž?ñúÖ!"«š‹÷Ô,Zmä¬2«’>öWºº±r†8RØl¿ôÍÚîšiN_Ø §ß)®SZn¥°ùšwÎò“ŸŒþl¿nâlÏoŸ×¤­ãH{öM÷Û¿ýÛñ¨”`-gÿvÛÐ>þ§ú§þñÿñŽÙ”Lüîïþî²z9žÛëÚ¬w†Wò-ȱ{Ùe——Ûn}YyεPІÛÿx)ïÿËÃå¯?ôwåοûR=fOpµ­(þr~¯$Ìb'ö¾ÝçW|þ¾âr•,¬B”.Qu^4ß:ˆ?P]ˆ½e]ŸâúϹóh•º7¼éÏPâ' Éï"_•Ðá¯õ“ÀïæùøÖMBî¼§ïžÿq| ü(?á•È'Œy|7‰¿%‘>½ƒÅ>ïÍ–±GýÊ<Ä\|]âa,ôRnŸûû0Úö'OŒ¤Î.Â5)Þ ¾98üžî à¦?§¦q¾y%ècËvŽ­aO«žEÊ«…M‡î>¤,Ó'[}ïd íÎ~Ð>ÎYðù²r-çÕ=m @·®œa£lÙ²‰3å3ø^ãGãÓCü”7rÞÜA2<„è¸E¿ø(‹å’‹w—G?T.º §<ø… lŠßn}áw±ÉðòïÞüÎòž¿þý¸ÖvFq~›“PH(%Dùa¬Ò3úÛw1X˜ë½oÅû«ÝQ^ûêWDg±+9qË\Hì$¤Iäd.Tþ‘[Ö÷ÿ8ì2g`OaÜ6pJ3-ü:œŒCŽªH—0`~Š>‘ÕŸFüºÐGÀOâŸø¨˜‘V0ü¨kÀUÍz‹'œð¸Ži‘²òŸlƒÈ›`uù'F/(;.=Y&2±÷ßÍ)åkÂú +qÍüÍðj¸:¥™÷o|c0ö†eLßùÎwÆ·õY—ùÚýö4û¦02¸Þ_ÿ±},ú‡pÆÉ x‡€Nq½Œ.ñÊ$xR'™à}ûöÃ+#‘Î>—.óµû¦gœ’7ê—¾ô»ƒàÓÅ[nœéìSŸþ»ò‘±|æS•ÃH~ýÉŠB b¥.Á? ¾Ä\âî|SÁ.FüžeøŒ?EîGðdz6ô+FAR£„àR~J®â'Žå.‰ô¬çE`½‰¹äûñkaÁb (/ä÷ÑåãÉ>†´}ϣͿɸ¿€Û¢’ð»J÷=”`ì¯ë|Ï{øù޾¿0n|…GÂý,œ9²GI¼*ÌÔœïÅ ³ < ­áÞQû³¦‹ÿãŸãæŒqüuiK†°4 5(räÈ)îAñ˜Ÿ–Oeª7l€a¾ñªmƒGºXBÐ×â!þTñUˆ±Û˜+"ºp¤ ß¿.yž îí=šx6ŽgblB†Q D8sÇ£GO—ÍìóoC*09>Š&éF6s €ce¡AŒŸ ÔzB±îf”nº.¸¤<¼ïöÑO—ÉÇ)¯üž›ÊýÒ©ê 8ßÖg‰Y^.âd¡“‘p£xP¦ÏV¢sãý¨ûäïP¹ì¢k2®t%Ìï}ï{ƒð‹C‚«4Cf@½9ø?ÿ«Ï•££|Ž“Ÿ/?üŠ[ËŸýÕí‘.üZÎv”ÐKÄ ;QÉØžjÍ{â`5'ó ±•!1Ÿ+}ß_Bïiˆ={öÄ J…'ãÕ©Pš!C#3àû<gÝ\å§ÂŸLVóûØÖaß)Þü*û<ù;Ï{:xsâAmºñ˜VKâ™Ø`<€Ixx^V>ÏIüIŸ+¦@ÄQ~–]# ŒÄ ¯Ë:ä³q¦˜—ÄœMׂ©#[xynÖ1’­7ÊGG·bK§¦¸`VÛÕÑ3a²mšáµâš°ÍðZù2=}óêòY_)•,ºO|â!~7;¨kÂwznÆ֙ǣt‰g% ›¾ãG|L°î©Ôñwå•ÏÄ€ÏËå/oŠðßú–•}ð“å›÷s›çIßÙ#nd‰ÓÍü$L,°c_nçWþÂH|ܳgUùÄ/!w®2Ï1ò8'™ç(?{—Jr7à‹ß³ð—áK´”8ÈL¬ìáéç>Sý¦ÞF;\ ¶—AÞÿ-y°­ÒÙõ°«nY8;+SY×5¿1+üñhÞC¤YoÇÕ“ül¯¿ßw§‹ŒÇøÉà1+oµùg‡iš½ãqÁÊIü}I›áQ~ÎKë£$ÑN`2zFYécg DÿVBO{Û§14;…©Á"&ã.`ì t!]˜g/GÏû¹, IJsÌ ñÊ( ÎjxH Œó!2-WÂáôëù¡Šàíçç}Áóî¬íÓâNž8qdûŽÓ Ð~ËôV&Øå,Ì'N¹*Ç=Þ³˜Û¶ hÕ‹}t{ %2Ó=0„‚Þ—Öä¥>½( : |ä ¤SåŽ/½üüOÿPyëÿý>VÊç) ŸÜ¿“@:'¸ãCL›Û¦k–Øs¥/¹éšr÷ý.}àÌ\û2{';E•I %˜5‰æØ¨/ZÏûž,ÃÛ÷°×÷‰ò#¯ý2üo~‡v«1­îÙ~*69 ‰Ó}þœø\¡{4ЉVBßÉ/³“xRÐzËHäÝ ¸÷Þ{C¿ÂãO/{ÙËÂö¸L€§ w g\úæËpú«áJûæUW]µL{ÿ oxCK|ߎ#ó5Ëk†;¥gÜZ~O3œù:Å™¶k×®8n÷Üë_ˆ’P•ÃTHù̧Kyû>T>þ±O”¿Â ›ØÇv>’K|%èŠâ]ÑJÀ]ÍB˜‚PÉø8/ñóÙ°ð»ˆsõzˆð£ü$èöOaÝŸ¾?ãe(¬œ>Ä2Äéûð§û|-¿µçù™†ºà9ºÙÐ(Zg§zá3#iúvÞì äƒV¯î.¥ÎލûØùi?ßWBk]Ÿ$ýA|çf/»ÄjelÓm¤Ûfžâg{Iô}G/'Ï}´ñÙÝÀ- Ckس·yCJ¦ÏüÂ-Ó^êD¼¢z9€žÀBµePѵ­ùÀ"j·,\>C+gúùôm­§Å8vlüŠ«ž1Áyý`X p[Z2ëï,»wp½/&c{Phêæw„#NýM\䪾‹2HíÂüì‰Shøörôƒ$Òw€=ôØ "ïzÿíåíï~3ðpmÙh:Ë•pÕÜü0®Ü§%˜#}åàCB h\7ÃHŸBœÿ^ù¢òýé‡;—©N|2î÷Nü¹>͹øE ݾƒÇÊu»®(ö¡Ï–ÿùg†c @$­ùGÆBœ*úþŠ3e>$^J7Œ—Àg\;ÂTlÆ›WFÅ_21©8¨"”[&Z1t‹àꫯ&Á-oL©ÀJL‡eaøÂF"ä$P™šà›+0a«þátQ¹*WoL>ÛÆÂd[7Ñý¬"äÛYÃZ.y}oˆ˜xq›ý ~4ž6>Òã¡ñGøú1ë’©KøVf„õu o]ÂÕugÊÑŸeRÛwxŠS2¨0M,”íSƒe³¤¶aâ1ïZáNéâVÂÕ Ö8ûŸ[Jÿþßÿûxÿüþïÿ~ˆâmóNâõµÊȲÒoÂg\»/Œ®=¾ýY™·o¼[ø×?Ǩ–c‡°|òãZÍÛ_>üîÛY£Jt°nHvó»ŠËà ö¦Iðý¹¢•à +7Žt‰K¬l!n‹ÎSq;Ê÷7¯½`¿­„ €Aè|ŽÞ/®u<Þ2gyÇùɸp¿õ¹.èqß‹ µ?Fõ"ËÝ+d¼¶dg™þɾŽôwMw?8%‘ÞÎOr‘®"T[y; ûØŽ×á_ˆ ?çÌäÝÇï1Â2D.ÊÌOÓÁúÁ<ž&·€Äd†ö™»œ&£]½5Å–¿üºÜZDñ)d2%ì¬è쌡E˜ƒy¤€ƒ,1O˜Ì]½Ü~Êv³x•_I‹p–þØŽ@UâÁA†4ÔqÞÝÓÆð¦ãÜ7FƒoÍÕžFzz†X‰O•c'Qžc+`+æ€OC {QÐЬ쑣§¸À«u!zoðB MÎ*)på/ c10Jرþä—xÚ^.Úµƒ}ÆýËÙU®Ä³ÝIG]0øønd=UüÐg¾\n{Á5¬¬±Y° `}$–¹ê·‰òŒï°1­nT…Üw?ÄßárëóŸÛ¹ÆñKø•4x¼ïСC!8ÅóÅL¼N°îë[¶ôÃcy*B"Ýœ€Û‹3ÝŸ{´¶—?™÷KUÌK‰n¸á†¸”(ãUN©„mìDßtIà%þìÄÑÎpᄌV´µE@ZpÞÃ09Ðê4U¼œ¾izñwéOEŒ%êK)Y^à¯%‘·®C–Uå­Š¨Š®˜ƒ–ñŸ¥b"´T6º®®—ω¯JXþ·•7á›y4Ó𧧇°ZÉÙp’5ß`8CņyµÌ‰ö2›ï’.ÃíþJp+ů–_"ª1ª×½îuQ¬ýT ¡sŒ™w¥:&Þf¹·’ß„])Ü)¯cãùÏ¿B3ûö±Ôúúç÷ìüÞcåú³Üù¯³òÌá<@IDAT–8Hp%¨[ùíáçJÛ¯æjÞ©L€?‰•"|WåŽu>ÛOðAà$¸t¦ ²”p;˜Eø—–‘€x.ëe~Wüââgù+9‰©cbõ³Þ”µN×ý\ o¡ôÿ@m©FÕsd&:¹ñÐ>¨Ý ¿4ÿùNpnqzG” «ü_2J)ÔvV$Ÿå¸5à¤ñˆò'&謴µuÐÖ‚’'aa̳’3MBîO†AÝ·$öJ\åíÝ»·¼üå/¯´@ÅÁ´- 3[É8$ÿSxŠy<[Ÿ´‘D"ƒX¡“nžö'Là—PÂV1­âªw¶,áÀ—í>qQOãO ©ˆ?QáZu¨ë”ñQêw«€[Ð-°ö@«Žuý—åoæù»axŸà }naw™b2Ü60¹`B«q¤oö §Ÿ(›Ïn÷WÊß„S2äõ´/yÉKuyÇ;ÞÆq.û\'| ³ZZf½pöÛ‹/¾Ó¸/e<\‡¤­U=¥|öÓ¥üæþª|êcÊl§`Ë€=øf¨´9»²”h=ÀÏY_‘µcx˜ŸNB›Ä\7‰˜Ä‹yeÃq²CØ8b¼ÈÝôÕ^¶y%€ö Dn”Ÿ=Â>'Nñ™îOºáO¥O˹Þ4™a]õ›WB+C±¶ëº˜œ×Ç+ÎÂõì¢VÑ)¯ ’ˆsÛ™ÄihæN²Ýs@q1õù9Î6ô=lßÙçÇ©õñ÷ãÀ÷}*b\µ'áò¨äçoþRx ý0ßù-zöWŒ•bÿy˜&Ž.N_,»ª4~\øWû²—?Ï÷ÍÔ„¶zBj5ÃbÔŧ 1ÏÉ€E.3›ç8`/Šƒs^$#õ6l) ˆ ß¡ËÑ~[ÿuža.h¥`¾š˜›™±CwgïxºÜÆ`×þ_æÞJ¯ãºó¬Îhäˆ I`ΔHI”(Y’e›²F#GíÌzŽÃ8íŽå3özgw¼ž™õYûìŽÓ'IÃ%K²eZŒ")‰I€AhÄFÎÓþ~÷½úðúCwÔŠow}õ^½ÊéëV"ì\ɘ>ULš1•=þ,(ƒÃf5Ї,t3çÞÉA@öc-ÿ<Îø—Øv!µµ1×~ÊË„ÈX3w؆È~NowØúÙZxôåÍé]·]•þøÿý vÂy„éÅ9ŽHÕ*¦à3;æœÒ¢tªo(ÄøÕQòæ×©\4¦)„˜ã‰Ç#G92¦Ï¸e:†dÀwS¹Ç*ãFkÌžüÖ“éÞO }æ·ÿtT|²Ûz]w”Z%nk4\çð}6¼lD@þÞ“ßå(ã)L£’‡ÌÔû;Ñ»L“S2±ë€ü3,Åÿ®ÈVêá¹®X¿~}œ9àZóÚ)Ýyþ8Åcj î8VÜ»?2ïü>Ïñå\ìˆ~I;˜Ù¸Fåu%Ïïð±üÁ¡nc.÷™¢éO¶Vú]ûîCö½VªšUüÉvâ»þU¿ùœýÈWtÝDøÙÎYì7 !.m÷|óYi¨éXjž…ûâ>x½vÝ{þ–õªýl–õ±¾Y·d2­G‚þ½÷Þ«µ ׈üÑýQ~%iÊ~žM×q¶3Þsþžuí9ª_±bUºõÖ{˜ªÊÀìf–~ñó)=øÍ”ž{ìétä”íÙ~ÃÑ&@ϼ£Ë¸…8{yF”1"×Ïå(G³“”GÙ>\x¶Ýo¹ Ö€o7`ÚØ)I1°_ËŒ„q‘Áø|ÐÝûn­Ìbô¬nnXºãœxÖ¾gÕÍFgvŒ‹q߇ù"ôKP¦olrþ¾ÿï‰ÝM„¾œ}W¶mœÆ¢)éIŒ{¾L¬·QµåAÞ™>⽆.,@™"½4â•¿Ôoì¸Å±È+ÑzϹ\¸Í5u èÛ:ˆ0~* üéOr±¤L»ˆ8•Tñ¿ÔÄqóôC¾÷t¹Ø¯Sm²\M7笠šš®27×\ìç¢@óÝÀEë=LéJ–P´ùx;ó§þ[y  `sÞéB2À.SVvtÝ=C€º£r*\–+-ç°%+„Ss+{?ɽ^jxHÐü™S˜'ïEì*¦¦±BõÀ>NÝ[`éJ·¾ãiϾý1BýÜ×OŸüè©­µ‰C…¬l,×m·Ý&ˆÉLd@xýõ­Øê£Ó¢’ð£ýL[âHàýé’åóLtfã3ô,æ´cª2ŠÆC¿ýúËp#wn…`Úcrúâ×O·}ôˆÏ¤ÚÔÀW ôWIƒ$¸ ü24†)Hûnú\¼äÈÏÖé’U¥[v¤‡ë‡ ï›!Ý9¢¯.tÚÃx(…p»U>[àÖ[oeäuKœŽæÔߥèÔ˜[+ò&ˆ26¯s~[õ¨%%Ðû ãPÚÕ~ÕßmœE§é[AúÎÑù/_xöƒ¯¨â)^KóÓ@ÑÐVéQL`îK?jþò1-ã«y}](B©ûÅ]ÍnþWc–? ËÛax9÷üĬt´ë4 †yˆ#´Û ”d¸™òs½î÷z³ü^ýf>[®wÞyg\]›ýÍú·¾õ­ØŠg{Êí&û“õªÙ,ëÙŸü^¯WÝê¿‹to¼ñéÖÛî¤/È® }Ãó)ýÑÿÆa:Ú«à Uóa'J°vtl¼%Ù (‚«maJûÚóÝ>À|´óÏyëó1”£o•ÛÚ8¿„Ñz±:})f™jF%#á¥7¡ë¯ Ý.3 PyrŸ€'c ]A¦8Ù/æË‘~%ϧÃ3Žvït^á—ýƒ#oý²ÿ3ž£™" Πa°¸ÿ›„ˆàaþ/ûYp‹&Gî4­ï 'ŸÉbO<;3 ddž ¸/£L·yà"?â ’^©w ?®¡˜šš&ÙŽþÑq=eþ†ù·²ˆ¬så 3Ðï:€&N†°Ì(#ìÛx)P#w›´5qFqäãø6j”«‰ˆp®<Y{K¾™kŒ1ì´a{Oz¹51ÖÖ WÕÄB F†¬¶ìcZ ›•sgaFc?rø×s¤'+üÝ«ïþq;_¯(#9â=i=+ûüñ`~â¥ôË¿ûë0í06ð<í»ˆÍx’™ôïû±oø_\ß›;2íô"UH'ަOÝóÎôÈS/Çè3»­êª·ý ®‚d¦ÎÎÎâÒŽù‹ #ÄJŽ îï>«ˆmf¬uØàpv2¡îè[0t÷‚€¯4Ãø:Ò6mš‡åË–¥øËßIó®ùdzýÒâÛ~1î[ÐsÓÿf€j¤Í^– (‰0^ž8èHКdzV¬X‘î¹çžX¯ñ W!ƒI!Š4Ì‚”P 4μcz€OŠö£ øf1ëÈr àåÙn@?²?ü3tc5ìzŽDÍNa~éeL-o5P'Ì‚ ±W'Æ/ê.zø†…Íìw¼•iñ9›çøêG6ôhG3Ý”îÂF6«„+?¶§ù3椙­o¤¥3—¦ÝûS#£™\gk¦ü\¯û½ÞÌz£”̺£_Juî¾ûîìUèn u¡ë׿þõ8€ÇižLYjVï¯ß³ÙxÏù{Öµ—~ø½·¥åk¯Ñ¨F†úJéïÿyŒ hæµï§ó+O¿ÖžËx;JæD·~ÛìAÔ”#vßÈ'K©lÇ1"ÔÈ %óàw™•Ü ‹‚KQ¾‹üòQ·¡ÿ¡nÜ(ÀéÆ0O—¡çâ;Èñ|­Ø.¸3¤­@·/sÄïùøÛÑû9(»|GÊ›°g>,GeF†Ç:ò¸¶aTúëP¤«ÎR¼²­} ö62ͦÿaܾ1–Ýlæb\8…¸åÏÒ3ßY[2€Ê³«ø™xä݃棿•Ëkv^G߇®=O\]JM“e{ P¤Q‰ˆfÜ+3y7ºkŽˆ#‹Ï‡Í7 qìwgb~ÛŸ#ÿaÖÑp«–`¤Ø9ÐÂ@¥H¦Š²¶`˜Åš VH1G<(H¢Zz ñb(*Ûpî“üSáÑŠ½óôxAëí¨6n3Ê%Š„9ŒÀŠ9ç/°+Žñb ––Æ4sËœ0óÝm€V–ˆ´+jH­ØÌNÌI¯\¹2ö¬¿"{V°ð8ÇKv¶žš£o™ EØ™^Úð§¸Í¯aE鎨¥A€áÔž}éž»oIÿúwÿ,˜”øP÷#*Y¨gœ³×¿¼èe‹M³ ¹§³ó½éªKW¦Í¯uŒË\`©F2vЦÇNÛgóÖøÊXØq—u—^ ø¿7Ü5]òãéò5ÿ!=óÂkÁ<èîGEÆCeø¦)/tŠÂ52^–ñ’,·û£Ç{ ‡?*FnLŽþ%ËN3Ó¨ÒTÀ.…ŸaZ¾‡'†S¼ØzX³”§²LAiOkaÓ0ã¥æY~öO=ûYš|-Ìò›®kæ9-UwåsØ)ýK›µ„­ª#¥Þ†‹ÓÔ–î´{ÿËr„R”ingõº~e3Ÿ­7ÖW·ÅþäOþdq–*dùâ¿ë?d8eüêýÐzÕl¬çl–õz7N+ÜzÓ5éý¸‰ˆ9J®ÒF»àt÷—¹Ö–‘ý0c¨‘Nô-U;çú¬OÒL” "@J›Qާ¡l÷~÷$ºå肊$°5% 9Š'"!Šß.9úÈ%AÂ)…íèú»%tJJ ¶·¹†ÊøÈP8P””\ƒºµ˜wî’q5ê‚•qÔ_¤‚‘kŠ­eXLßǧÆËˆù/Ó_SFRíØ¶;(¶“PÿúßQý뜙f×X܉®’ázÝ”3o.#¶w WÓw„÷­Øu´/³düeddª0ox•ã~á:â`_¦ËC€fà&ç5 øât@¬¹°ÁyWÀ‰A3gû³É¼h§|jñ¡•õ9pxæŒ6Ù€„‰[M„Ð .5 &~ã7ò¢4Q;M´ñp¯U3 â;AGgç~t=þuFûìds§7ïýÌ¥´´Á òÜJÎz3à @¿‘­~CÌõSwNéJ çsô)Sƒ”‚à¹hÁœ´mKsZ´p. ßLÜm}ëvj{PqŽïM¾ë†ô'ýõˆ­@ìâ8¶‰“ÿ*éˆë…•@°ˆP Ê«ëµ2À þo<üƒô‰_º—ÒŽÒ¯¸<ý(øNbT_®Ž’d ‚z:C„™€î*Ç⃷§¯}û)¤Vô‰É<Ì©Œ…ùLæ±yàûŠ¥ £ÿö'ÿ.ýô¯üFúÌ'ïNÏ¿üZ0 U ÈÄ¡ûW㑚‚K1Ep8¦¦#h*™}uô¯È?Äü †YN_´ÒDšlL§íú, °l]¡Ÿn[ÙÝçüÊ~†[Ükîsx—²™o•¯EHÚ©ºñ¹4«¹+ýÉæú™cfuþú=Sö#¿×ëÇ:gpÍ óÿ©Ÿ…¥-Z˜Žñ´³‚Ô~NoõYѹõâgögC÷Ö»zòV»|0ßúl™VýÈ£üªYö#‡9ž®=ã¸páEé¦wÞžnâ†Ê‚%Ì>˜Ó¡þõ(j+J³)髌þ¶® >Éhó9:Rðc„®tÄA÷›"÷Ö¿‚ v7Jÿa_ÊÓÕèèD´‚ÛñƒH0`ˆSêZñËãgûfa&˜ º®Ë¸úQJ)ùÞB¿ûÎyœÉOžç7¬Ë…¿µ‚ç5¨* p/âïtn¼~F¾Át˜–¢Ÿñ 䤼/E š“àßöQrúç‹z:/VáëßXt'~“úÿLÈ÷óñ¬ÕœV—‰ÿv¾8½`\Š’A±ß3__BW"0]©‡žOAY_ ^o&©º%_‡H—‹+ÿ›ÇÍ0t HAÉ÷ÎÉH«1§Ï‘™‘á@ÎÀî±Dþ ±x\&€‰Gùìpϯ’}0ºX4È ª¾{ˆé‘˜;¶ÊH´qÛô˜í5÷ ~Ï 5»tz!è‚2Ãޙ܃L×2˜iæ Øêö{ñl?s^¼ÍÊBS`Å}_ô=žpF5?É™ôˆhÚ[ FÁm€ÌÍW]BÚ ¸wÇ5;%{˜ó¾õ†ËG1‚‘âK;A UɃ‰@xz·wôÇœgþn\ÿ™i…Oü›Ïrýp; k£›dòH7Ûpdœ€Î{ƒ) ‰ë¦ç7§»Þq5óº$ôìøNÿì •f¿<å`Xvô¦uÙòÅ…¿ÿÊ#H–¦Ÿþù¥ûûQ›~Èñ{+tãæZã£D@ÉÀ$˜•I06é/Ëž-€˜/¶›øn¤|2 DƒÃ,êöÔýÃ^¨r /Q¦…zî⟚Ÿ¥A„_†WH'´TF¡4×jÅ»Òåh­HGa¯êgÕ–ñ”"Îe:"­u(…“ømDœ¢ÜÉË–‘ÉiÆìùìˆ9>jÑ ß-­NÇ|ä#iXVñ%¥ûî»/¦l\,š™sÝdòÙö$Õ›Ÿ‹™Làš5—§Ûnýhºô²Ñ tþ õ Ô]ééô ô¡õM=ÀÄÆÔréqþ|ÿS4U1 àMÆv2ÊTtÔù ú1”"æÐW— -FîgFœéYT`³­qJ±ßEk 1w^^P÷^z™ ³`åsÎS÷xÃæƒ€oäÉúl¹¸žÁL¬ÞW\nïCqÖI,–³ûVü¯‚É(ü¡ ÆAt”íHØ2ÓO% ;pošÝj¸½®ßûæÛq!•žoÂÜIJÁ‹?ŽÁ¿AE¿•úÅTºÁ†•è›Æ²3¶Y±pÑr¶Ÿ·˜~Ó°œx:ÒžÃó<”éð\×@8hÞ‚"ÏFˆp·[ýpÊô¢}óÆÄœB¹eð9Öà–5f¶1Á_»öôúÀòȆ# t& …‚bü0Û­“½Q÷ °«^øæÅoØaAé:üŠ—üc;ªôÙ’ð]ÙÊùÖ/(ÀA0Çí¹®ÒS|ÈF­€Ÿ Õöb_; öº9tç6–,žÇî€ã1ŸÞÞ. STƂـ/k<=PÎL·­+/MG¶duþámG(‚âwŸy%Ýpí ޽û€‘ á6GÌŽR•Ø©i.Å/qègo¨ài§™¿kåù—¶akjºxùÂqw®á×®³ÈÞpŽ-nt®Ü8èæK<•~ï¿ü[ÒtºóÕîxdœTã§ß¾ï¦Ë¸+rWO-6¦Ãi÷¾ƒéÏ?ÿ@ú¿ß÷KL‹L03C2^?*s㚥‹a¬&#êuï= &7«^œêǨ3—K. 0ÂAQb¥ Þƒ·ñB+P~e1› DØÅOýÏäsq´>j¯ü¢òEÛ9nñµô£j–}¬šå0²®×Ù^63 ×Èœ¦ÍhIÓg#äm]˜&q4voÏÞÔui€)Ó‚ ®Þ¶Wõë‰'žHÏ<óL,È´Þå´«WUÕMÕŽæõïÙÌörà 7¦k¯}»BF§Á…yÿë¿Oì­gx~€C ôÑ­ÍÔ_Mé¶E7ã£_j,r<+\£—ðzögÙ/AÁûä—gtFã1r>Œ.(Žþ›)§ùnÜTå9óƒ yÖß½¨I(kŒuгÜš¦#Ñž%èv½°à¯ßiŸQAì ÞžG† ¥‚qwú!ðw¡ÕŽàþʼÖÌüs=ãËVÞe L÷Õèc§oä!}•Ïg4ü4ùJÐEþ(M G‘áf.x7 ÖǾx”C^v—¥èú»ºÔí£û*<™––NtóÊ4ºHrfäkˆû-‘MŽÌ‰Ûaãz`§ôS)ˆg81]<âŠ~™ÓAпµÅA”RTÃ#ÇÛYàØ«$€wA ç6°f`&.vœ1}0„`xˆ0!ýÍÝDäÛr07´3½Ñf´»ÝÙÊùÖ«9|¾ÃN‡è^yñŰ³ “‹Ž±÷vw÷Å-½LL¥²*Âö{3ó0]ìÑ”æÏ™Ž/Ó™6™Âs»ÀK€¥g_ú^¬Dwš¬_èÙô‰ÏÜÃ9άv/© ”y®¾š !êd¾aÖ”Ö3íu´ÒíN—­YóèU·ùÙQ¯q¯g”(ä‹N? ðO½ôžÛ?>úì¦ô{ «Óâ³5˹ž™å¹v÷ä[¡3‡‘Vix÷÷¹\édzô{/óÖŸ>ñ‘w¦§_9vÃÂyü1󼾋óró)á6bRÎÍ•FS¶¾øì™ ÅŒ@¼“癪~e³QzÌG™—~x`m}‚-×…B€rİSú±©Ä©ŽßrÊ5/Ú‚†˜êfwÚÍtøèÁ´vÕzîi8Îv¤=é’+¯J¿ýË¿˜?‡îÎŒ‡z(Fößgñ¥uPÊñµþäçªùÙÌl_^e{Ãõ·¥›nXNüg,Ìûú?p ×çŠ1õé¯ÅSÃEÄðŸô1ºçõ…ٺзñéXúHÜô0ÚôºÚ†eè<¿yÚAþ¿„3¶RÆÉ}†.  JŠ{¾žÖž€ªy;j9j:ªµ¥¹à$¹¢I)@º¸ÅîZÂðÜòvÝÒ¸%k hœ»2T1u ]ø \JôW3Ý'™¿ë·Œ+æ•w Ì8“`¢L¯Œ“ʼ×üÙG^°Õ/¶ b;õ.$òJæñ}J†HòŒ¶˜3°ó²†x0‚§¯6X™€êBScý ýt/å+ü)Ö¤&ò†áujšÊ´ô)×Г"p¦¢½%Y„Vû¥={î‹)‹)X_4Â=Ρ Êân2ë8™µ(2 2s/˜ˆñ¾™YŽê{Yð×Ê·VŽiÓ§1âç‚:žƒGN¦©lœ2¹5ÍšÎÉ\dƒ{ñÕ!jV¤žE–q3`ۜƜۊÝi•à©]5I±€ŠÊÑ‹ÿ'Ni‚ š;ÎS]=éÄæ¤÷r¾ÀßÝÿÏѱfí4õ/2U2Œž^¹Zªþî}L#R»0³a·¥KV,Nž9`¾LDÆÛ<4~.Ìr½B63îË?rˆÆÝÿÇ8¤§s²ïñôï÷çÒû?ñ‡gLSLÞê›ù˾ð3 žG‘õÁ”Ûœ Ò,cõçÑð0sŽe?ð£jX>‡¡îP–U¬7Ð/”>h'ûö1Ëáë6Ìp[¥l?›ùžý3œš?ÙB©×Ìõ¯ŒÛDö«ÎG†ZšL]'9Š].G"í~ecš=£5Ò•o¿Ënô7· Í|K¯7³_~ùºtËÍw¥å+æ„›üÓÉÀ÷ïþšÍU_Þšþf-U0: †¥ó¼ Ðx„>üN»õïãp,°9íßQFˆÍ«€¾ßÁŒèwý9.¶ p:²%&O@ ‰Àbû™@ å|º`a| •G›¶:ýv¿ƒp1ú4ÏT€OŒV;Ð=ËÌÆ?GÚæÃá ®9m2*ÎòÐXo¬uU7|Gîö2Ú±Oò°Ý€6ÁT8mq)ϋї£x±(-èææ¡qº%`J(Œ³q j4™ªæ!/ø•¢<ÜŽ¸*ýO˜—±è[®I `ìÉë'kÿñÉé—o£žÂŠ¢÷ëÑ¡› âá>”iwľ}%úuè’âÿNìmD½LD±ßz3b=HÿßïV?óCÀ—2ÏÜjxs¤¤øÕØh_üÁpùo Ý»ÈÏß” 3EÐÂ<ÿónìÂcƒcê¹ÅůêÖÀ6è>%˜…”Ò¼AeÇi^%?#=½½vö„.4À±ú}Gš§L!·Ì.2ŽLÜwð8byWη¤Œ¾­ÇØë?…CsZôrCß…5…¹÷éÌß÷¤ÎÉ,X0/-œ¦}Ú :8G'‚¢tà°•¾+­¿tyzì*äIvŠ£í ]u¬ýÌ4(BnãtAv xE±Ÿß3 0=ñ[ö>òîë=·âÜAúç8ú­hµXñŸ¿Ê)gíkÒÉ6ª™¬?PJ‘× œŒS;ÒúËV¦o‡„ (ÇÙ4¸-Ë›ÖìÔ Çyw·rí?T0ß|x£Dζæhå?þ¯÷§_ÿ_þœç?8CJ1Ap?²OŒB3ø8 €qh4 "¢ Šgë y‘Ý„²îD¬5½ÅÑù–Ýåí~~çìGéw˜ëævŸ±Ð°ZÓ‚øVÄ.Ôée™…qʯHWãØ­ó}ÔkÃQF+ý‹ÒŒ–7X‘ÜÊY]Üä¸!M)§…rº³®ãü\¯ûÍúë¹7ßüNÄø7Q¿5=M/¾Ò_ÿe7£ûÇÒÆ&×µà y;Ü9¨slæbÿãÄ©¼¦/ïž# ˜óßXÄ}é_“ÿÇÔz™j3X¨î†!»ÔqEë2K‰ï‡ÑlûŠøŸE¹Ò|'º$ýP1*ÕÌ•ø®æ£ê»ÒS˜mÃDm5ö¢ˆÐÀd,è:˜á9Ü;êU"°e¢Ò?•µÇ>Çü•9@ünVóŒÈ#$u…€÷:.íïlF°±HP¡”¬A—)8“V"Ij¹šoVÐÐ ÷Gówd¥eظxü’ ‹!ÁX‚÷ïáUÆD°>‚R `û§ð€Ê¼y]Ð?„.óbzÛQy7‘ŽwËjÏîÆx…Ïä9ùíÂbÇB¦@›¼ ˜þÝaF#søƒýÞCC*˜Bîg¡ù m[Öc@q?þ°^¼gáRÇAÌ8ŸzÒdv —ˆ ?ÆéôÀ!>ŒóCßã)€r(„¬a’H?+ «™×‡Ø}Š™‹(Æâ'Nô°Poz—;‡9ž2u bû.T§ÎÁâ¦yé×~ –• äÈÑ4GUYüÞÇÜ$”>øžëk €¢çÆ^íe L;´"y” ®_ÕïžDøÌ†ÍéýŸ|oqs`%'µëÕ¹ŠÞ“W`G«¹¤^;¼Æd&ç(YÈÌŠGÛ´9}âžw¤?ù«/sÌ$yüô_ ‚'ñyÐPޝitM@‹¥žAo®$¼ÁôW÷=Лڬç¶ÜóLùÖ>?Q[§BœFšÍìjÌAw ˜•ŧ2ÓAÙ÷BWMRÎi~Îå’ßÕUÙܰ%¢£³"¥™î^)l„µhüá¶´ª“ÂaQ3´ìuõCŽS¸ÏaV-”Ïm3ÚÓ'•ÞE»á³Øf:½8 çkÖu’ŸÕ]û±jÕjNñ{;bìHOÓq0äáFjtß–ôз¿“N‘q6µ.VsĶ6Tš Šª7G +èf/t®¡Lá¦0÷}6beM-ߣn`Ñv;ðÉ´ÃÐ&r-x®Æ‚£ziÊEz»Ð5³Ý aûWœ.À#Q%cµG¤ÏñMàÕ»Ö<ú¦d‡¥ ü‚“`a[T”ïhV0¨µ/jG°×‰ŠøÌD÷„Â+Ð/EUÉÑôkè—¤›½(FÃÁ 4¢/A)1ÈåSŒ¶1<“àÚ)eòq8{¶®<Ón˜ÏŸ'…ÙNã ÈCMH¸Œ ¥-¦3ª“>÷Qtë“­Âr–©”I°ìL—y§;òmÈ|Q:ÒË;ç´lÃØüBEÃdç@3õvÄr˜É1ÀtvºEkBBÆY qø\&; be¿uÁÛØfnx ªÄ¯Þ~ÚÛÔsYÛ>í Ðum S˜ärÌ1M`ë­ût¡G¨{s¦™L3®ÕþnÅh¡c:rä`5”¦1§0žk‚»‘p½îq朚[áÚ(€ûÈÉ4û”h]úòŸ6Ð΂¯£b© ?½`ßÈé{v|v®®Hw_³]ý‡Gûl\fGèêy @÷<µ1ý^ó ´eYn$™€ 6œ1½ CqðÐá°s˜µ kVy€Mž0}õ áxqÓ–tÇOÝÍ–H:œ³0º5LÓá {†)hf•@ ’~iˆ¹­L¸KÁÆ÷w\•¾ýÔëÙø¼é®ƒ°,òèŸdD%Æÿq¾û$ ü¾ùçJš ½úaÝ)…Üq®ü™G¹Ü³ncÕ\e¾«g»ñÀO¶Káah¾”vÃ^Ý{á‹öy*ã–ýûåOµx«}¯ó«üXu~VÃ䂪üÓ¿€5/ý¯pÄ4Ò,Ȫ¬"2¤—^z§÷ÝÅ¢V;ÒÓô:òÇÿéhúÆ?~7½øÔVC[ŸCKù –›yvÄ)`YFÛ×R”ÀõÑ¢ãI&»Ž4¼h ñ|цÆóñ•èô£HÒ U÷ÔR·¿;ží±Ìm³Ò²ø Ѿõë Ò´3AS°›¤/Ýç(‡¢ý±T²X „mÎ|„MÛ~@UG­1š—óìe2-óU·Ö Ý:J^޾¥Jö d8ì?ŒOËïQ[Qæ¢~Ûoê·ß SÝxê¿e6…AðRòàÐã÷©âåŒß)Ìâ+ÍÀ>Y:ø¾øz†Í± < (¦cÙ›·=ç›Pî²¾Ù¾;ÑMƒi[ƒ¹é³ŽüJ¡L;S'q yn>·£©§å@ó£‘E˜.âô2 êÓ°J ŸÝ“ŸL;Ûf ¦…›iIÌ ¾¹àoÂ&˜ÓüXaÁA#H âÀ²Ýš³çJô.0±P/Y{.(¦;í`ƒè,]ñ<‹«pâ=.ò yd &Ç 4²)ÓS›XËGYqZ•d@퉟Rzü¥c1bwTïü· â$Gïßzô…ô?~úC!²ïa”íww8úÏ –óá{f½#kÝ»HoçÎùkz½ÃJ99-]ÄûQ*[Iâøí\ü“O>£¬üMÝøÁƒTxÈ9%í_A@&Cá~î¸ã§> ¨SÑ$L@Æ]ÅtlÛ¶-}ík_«I-œæpúbÑ4ãžsž{cb׫éŽ[¯H?×ßÏçO^YÄÇFG|lA<šV Óm«ŒXù+ƒ qOkR¸ÓЗ’ žÔôð»`r^Ôìò­Ê à!ŸÊÐ"^Ù&:ßr|bµ~é¯6rXÛ£kîÂB0~>—ï£,׿ÔÙ™ÔßÃ%h,™õŒ9+ÓìYÇÒÒ…-iýšË¸åŽau=þضôè#¥/þ·çÓî-±M"Y;tÑgû(¯QÏ‹Ó4wD+ pÇQúc{-nÎJQÕô­ é«o¤-Ðï“ÑÙOä|:ú? »Ç~“ÿ‰z²q"û}sà ø†_0ñ?öÏp&Mº6FßM§€m^™wæƒ`$0™2ç1kÚÅ+þ7ñ­Ÿ|q}€}’ §Ú… w‚Ÿ h>J‹»}jÊ|¸ Ó8Ë€ž]¹ ÞîAŸ‹‹ì«Œßʱ>ž6¼›ï¤LðÆk€OÓæÓ£žN"¯ùgLÞ›úž†eüb¶a”…1^v’®G1·Ïv}- B+ì•F…yõ%,žn~ÕðÂoòP3If¯†a)úƒÊP‹ð¼°Ÿã¥0Õà5¬HáWÍ¿rØúñféà`[j?ãËY­'ÜÜš~å3÷Lßù¥ûO_ûêCé¡ÞÈe'LeõÒ³{XJûÈí§•ʺHŠiq8‹éöݮ‘ª@%øX‡Œ½ ÛìÀÍçÒíÀÏð¦åÇéxÁÁVxÁ?Ó~ÀdAúÛü:†¾¼žÿI¡ß¡¿ÿ᩾4M[ðNX L›Sží_IÁ íâ‹ ã6?Ó­?Î=ëFóÒ_®ŸEžK &󤟖}án]|èB¿†áËp™ÿn3Í{×9P,¾} j"2Žö#»Qî £ÏB.žz…<] ûñ=txŽÖ+u$ŽE Èß‘^Å.£¢×@•1Π~Lö§íè—óa1j Ç0~Åã,pÃCfØ'ã{Q&$ ƒà óƬ %y;à€y•Û­ÌŒõ¶yªGÉHhˆºK5neÝŒ‹üÚÚ0C ¤@Ü ÐÀ"A¤±#Hlú‘¨é^@sÓ)˜J­…K°èC† Ifx6V[¶¯,q|eÇB2/•5ô…N°‡Ú3mÆ Ë€þ–LâIÑL?þ,[¶ íë<˜Nuõ¥+GF·¶5³p sÒðÂôÔÉ.Då½é7~ýW9*x÷z_U[—oÁóÊÚ—7wàb* X$]‘»€8‡Æœà¬gÂqžTiÁ\VÓWÉûF:™V¸nMú›/=X0 X°3xyË`Àõü±ƒ6Ê”ö<–ÖR›d×%œ¬ye>‰É–0/¶aÊOTÞ‚iS=Õó¡È‹šá°#›åúgž5É0–áå|7œ0ò§ôT3©ýò9K´k´/UÜú¦å·ðKûa-û¬­³ËdÓŒUé}ÓRSûöôÂ#;Òçÿìó©Ý3!VõÖ4NB‹…hÃ.ˆ„T³Q2žt˜!‰´ÞîŽ4=Äf ºöf¡+8ð0³ãuí¸8ÁÍÎúìÔn5à¤qѱï§)ºx¯÷A í}…û¶1›Uýò ôó›PøÕ÷e’¸£jçlϱû–Óëè[ñ° ÍrÞÍA×vع#`æ˜t`¤b¥¿ý½y$ÑÔ˜)ó$ à5,oQq;Êy{G¶”Oг‹‚Œf2fúmÞÚ./BÍÅÌx혇qh;~™^JÆå_¢›žs£a’:òýe#¿8ý·p·ö³ü¼ ¦¤'`J,‡ž?Ã]ÇVjF2=¦CÝ:g?ì?ãjhžX§Lsì#Q6ÆÝ´˜2Y¶ó]f(×gò¶o1ß,K9‘CE}ÔŽõÖ|à òd¿Gó6Ä–>¥4}}ÍH8h.¦XOÃ:/÷þ7À4Ã$pR °Ã¢@ŠÉ©ÙŸ»Ð°…{¼c.ZmÙoàíh*Íd—íç.Ü)€FÎÜ¿ DA ’'1,5]ý?•yþ“€z/s/sXà·ß£|!oócE ‚•€cnYØçQ»³gMI‡ýAœ¿™›æ<Ú×#~=àÎ;ï @|饗ÒË/¿L{f•üº5éÛ=CÁ:×ÜXˆø#Z —ÙŠxðhWØqÑÞ\Ô©®ècåÿ“ßÛ”î~Ç51­à’ ! øJy{_¼”?ƒˆ—¤æ2œ§—¨³Ý8]°kºîêµ0)~dç06ɞ̃iRÉ©ý½ûWcÄ‚CÂÉÝ=ùôF̧s´ò,ò^ÉETãìü-Õc¤M!m!ϼÂv"ÇÒ’Çìy^~7by+ ów™ü^´µÂ¬x¦Úè—/*ž%ßkæ¾£|ÏTÛÿ_Ȩ8K~è¶4·;ÊÏúþøÍgT„§^šùµøžãq.ÿTÝû|b`~jêRæô¦îŽ©iéœÙi€CŸ‘QfZ€6ÏC ±mç«Ü2(õ´¨õ‚–`¨]ÉñVtÒ.ÃÕþ nŒà@óh)ú%ÉرGëÑà X¤¡ç ¨ó¹a9  é¾ÈüCÔœ[1ý/~‡±†ýéë>”Z¯+¬œ/ž'¹ûPOE4ŽãÒØùz ƻȗ¢”v”iÞ†¹`í–ºÕ<{:ßu("K<ŸÓo{ó=Ÿ<'ceûö˜áì—ýоžmÙZ#sá~}óV&ÂïJZV¡kO0ÌL™Àn<®ÀL†Â8d@Y˘™.ãkù’±?Y]‡v›ë)u‹9d.cy49öK¸“™ÙHÁX¶N›§\ïL›y6•¸[€¸Ó ÌÌóc1ʼ’¬“–vY>V.~Œi’¦ýX§ œnñ(`÷ëºÃB¦AwÖÑn{ÖO†â^øãaMCq3î0sš¹í»©f¦ †‡`BÚ‹ ŽÛööÀ¿•ízŒçYÈ~%÷¥Uú¿Î³¸zK?_pààþýÇW¬^…<§qfXÙϹ>±ÍoñE ™ðF+*!w.7²՛͜àÔÏâÀ©ìhd¾fמƒq¦¼‡f¼#à-[¶Ä±¦Å5¸­qméWÐxºÓ7]™ùÞ Ñ9® .ø Ž™pîVѹ Ú£r/ŠϾ¸9Ýþá_`Áˆ<Ìi@ðw$¯ *vÏ%:ƒÑºÔÚÖ‹3§zî§|.Ý}ûÕÄMÑ¾ÔØ$o§62d›2}Äeá5·aÔ”fqB›ñ‘10í^Ù†yOºóæ+Òý Ã) ìþ­Ô _m¡¢ò±½•ϵ¨d3š&fŒš´ à÷1^ù’% µ0t˜?ª—ïŽÒ«#þÜœ=H;Ôþ×"ûì¯õ³F˜Ç½¥A”Gícñ ÿ57e€¢3Þ Xu£Ý¨gŒÍ¹’ +m‘ó=I jš$7ƒÝ2–Å{Œ.kù¨x>“ùažmÇ6ÜMˆ¥*ó\[Z¡ úšËh9µ 8 ‚Þ"”}ˆ«ÜõN&ÀRsÁLÐw仵;Î_ÎóE(¿oËFÃõpCŒÏ ?•NÜų€:15À‹4ÝLÈŸ†ûÉ\WÇç¶ð¥ù2jÏìR~CÏŽcõ cæÓC ãéŠ272aËQ28êÖÛ\62Ròî4é7O­›2¸äÝÐÅñ^ì¬ Ž“#Äè>ìZ>Œð[Ùqгf¬…âw;wÜÛ€T1Ÿ ØÈâò„X_ A1î2,NšÄ@¬—i×°5ÐÛ›ŽpòQî °x&ER1Æûܲ º£bzÞ­™šº¹ÐçxSSÃL;Jò…Ì¥ypýï ×úNž<À!6Ó8øTL ´3Zîeßÿ4¸mãX7‹ç΋m‚ƒœéÜËáîpᛀ(¨:gÿý÷Ç g^ýÛ××–~âÇ?–ž|uR*Pl‰ëÝyÇ@‘)éX'!‘°pÝZwõÕW3Íjñô6Óææƒ’ÍÕÕ{¼¯[ðòœrµ’´O*:Ÿ&æ‘<¥Í; :;;kkúYÙýÔ³ÓÍúxHEª~×?gf£*ÁÈvüvF$¥¨I:4ÿ8 @â ~·¥ûñw§–eçqþ2·6ò ­ÜFÎ QRÆÅÂN6-¬X*¿©?…_æy€°`¡ö\š¾T3·ÊOxjçQRiß7£W-Óêsi»ˆ—öŸ2¬üQ=û§®lV±_Žÿ» }jr™È4¦w¦ÁPvìã´3*)Á¤ˆ©¡Y³ß|ŠÅVÖGI~W9u¡ÚtAEæ JvÀ0Ìt§ëѵ?ÉXhÿ45Â/´~u5ón@‡h5¦Ïaá/PE^¶]}jfüw23@€3¸Tå‰\…废h_ÀÔ­h®&—ÙY޾]Z†ªR{åE u$ºý” “~d–¥£{ßõÇ+ѯB—Q’‘ª’̃S /£;šß=ÃÒ/»jÛåb”`¯?9­¶ß½(¿ËdlG߉Rrs]&ã6Ôµœ‡OÔ}-šQ©ÕÞèz’e¤é†qþ:?Ùý(`Ç쿎òsU×¾~D}Dg-C’ñÐó@ñ©ð+DZò­æ^³ì6û…YŽ_ÖKïÎð¿f>ÆCç_µìT:u 9½|dzš¹š¨^;”z›(÷¡%¸PÔi§(ð[¿Ôí53UN¢ü29=Ü$£àèV°sÖÕèvÈõäµeÇì÷ù(i4ø›/Í7 ð¬lòYÉCqŠr¼ŽóÃÚšôKs-4‘DÁøÅq¬2΀a[íD©øä‚ùa˜c‘öÅÃÑ÷¡dÌCósO€KŒ`ÍÃ9øiú/AÕ3H2DJ7õoJ°ö½ ?V nA-Ål5J¿i+–ýŒú\”]¸þȼlG÷£tËÊ©ý}ºî· &£¬“‡ù´Þ‚/¿Y„V,¾ËùI ‰Nã,ª›AŸ•¶Oó)U02,–ûVÌ_G7Žna” »+ž‹´vó,“¥û0¯"¦,¸õ/Äÿ^²4T˜'Né‹u/ÌÑÇ ò«¹¥‹âBŒÏ€~.ðihÀ>ŧTySÿ\Ño{ñÌÿF£ M´ ¤Á þ©d|ï¾I4LD? ‡92XW£(·w +í>Û¡Ïfj¶‘lxžõ·> ï¦3¸V,:Q;¦¦–4¥}8?åõ¿Ãiá,Àõø [íN¦œ¿|ôð‘Ã>Æœw y=o+7ãÔ>§ =¥‚¯Jàv¿¿#ñ¿þ—anIzÉš5qJž`èE<Õ”4Œ;bþ½?˜ý ½6U†ÂÑõ»PlÃéNíëØiQóî2 ‚­£»¼8OC®$Ú™"0nëÖ­ ©…S’nž}i+O=qõqç~÷‰Ogü˜g2ÎíŸA8:Tž9»†‘§—uóß`ÎÌié8÷œO’Ñ‘A2½Æ)Ç+Ç¡HS‘pӯ¾¿&U*lWMtS‚¿”îô,¿ççˆbÁ©°`#p_Kÿô×z¡.…ï>×…Sñ‡­2Î¥›ZÜêÜ•VϪÍXИöú[ö¤FÛÇ6@D|Áw6ÊQ¢@`µù;׿ ] ^ãƒn;ºèúI‡Ëè¶ýs„&cp)º 515½‹ïDaàZÝ È(nNéçP ÆqjgxPá>ybõÙ½]ós!G“‚£ jÚM‹d{,@¸Q H‚¸mÊô» ðå4“qÐS¦Ù‘«õÀïæbx@(¶ñéŸRcì÷rÄ"îE¼{8ÎÊøZˆ½ K7/á‡öážýƒmTÿeì‡ÛêÞ@wßürôµ(ËB»€ÙÁûó(ÁZM×2¾¿ÝòM¦`¾ÂñiÏ7—ÐEÆ›˜nä‡*Ðr7î^C=1–Ýlæ>~I†ò ºyaßiÚŒ—õÑ8š§–“LŽS2 ¦Ye:”x”ù4Óæ©ŽÒcš„{°Ä n¬».œÍhÞ ‚8ñ‰±Í­‰g§šÚR‡ÿ°þœöŒ[ƒ¡Ô Ó¡{¤ÒÜ8ÛFysàA¿ªÁHx€Q¬©[LÓ¨¶Ìûxä)€\ˆg…¸`ôv`Ü ÎÙ\§Ó´“íî:+-Ýf7 àì†ãêeÝŽîæ{r‘\âf}³ÝŽÝrß¹¿ øÎ{3Ù& ’/n²0§¦‡þNzs>õ©O°víÚôž÷¼'DãJ º»»8†Ù+.—‘pZÁø}ðƒLÏ?ÿ|ˆìݤ¯Y›¾úà÷â»%ª=• €QÎ3fØѨä©ýwÊ “àl'ð®›Ö§ ;j~çïꂹëL«q¬§F$½h$u@ÄH^§œO޾_{cW|_¶tnÚ¾k/áÄë[þ“ó(ä»ñÊqËzþ®n£• h.žó¯ñö»ókº­RÔ/Í*æ^iÉ2ª‘vxÏküRø-ÓQ’q-G…¥Å ùöê̵6Ç0¯8?§ÇfŽ2™¹—EI¡}˜£«‘– t+~¶ó£cu$4âèÔÑí*ôkÐíøfÊ ä(”ž?@Ãúgž!˜¤@IDATêéc¤u ·‚õiÊþK?.Ç|-úÔ¹Ñà#…½ø’¡+‰^ €ægcBÛ‰á’Ôƒ=_¢ùmco;/.n•AŶ'è"Çxš¶+óAfÀEv29æ_•ÜÂç‚—0,v nH`¬© Ÿ`jC·to~É€®`-š§ºL•ò-¿ö<“¿ÈAOPÔ¾«àoB}šgÁO2®2H;Ðw£LƒƒMAõìÞŒžëÈzé–ëÊ¥‰¬Zßâ±he¶Þ€]¢ÌY9|±·Io`jÝ’IY†¾Œ¸nG·U›7ÆÀ|’ÉÌÓ¦Éúl=D™V/í[.*§cÌ•Sæ›eeù¹ÐðƒKÝb›‘ü0‹‹EÅ Òy™ƒVTìÔ.˜Ò¸sP+Óý}öOÄ …Ú°×Ãb@.ô‰ë¯}Ê”ÍÔËNƒþÚFt¶Š~†W?Jƒ3ÑâGéû9ú¸ZCƒÌ:óǾXͧ×5qLãÑãWë¶ Q uqÉ@s«ÅqäHÜ®y #v J‚ªºß\(ù¾‘´ù>©Â6Ö<úè£ÂK–,I+V¬ˆ{Ò=ÇÆb;Èq.ýqä®è\¿œ«_¹²8gÀ9ûÁ¾´ëû¯²ÍŽk)‰|.k§ Ú,Ò¯ç¶6#cƒ/cá¾ÌhÄ~ŠÊ´'˜‹fFÉ^M\OÆGéÓ†e¸>gÒƒåtÅ+;Ž£`XUÐêäˆd;ëׯIß{b9»+u™ã‘ãb\« ¨ú‹‰LÎ߈ùm}©RÔ! Êõ©ú½øàÜ*y°²ùŒ›jøõnÃK õ¹pË'œÇO5¾xfg\ZÏþg¿JʯoN;À¤öýmiê:N<<˜._Á57\"•ÚŒF[#ôä1l;°ã5n¯ók‡*˜ø,˜8gÄÃá)R¡\'ME_îœÿô¥(;]‰×:$@­D¹ú¿éúÒõÄÈÁ_Vóô\ííÌölcØB4†n ÞG*þ’çW)“mgÚßÄ4 ü®Ö߇n»±DL»!˜>Aêªòí rû¦Îßë‡mT0ïÆ£Òðcº~÷¢ì÷Õmÿ*óÊÓ·’Ø‘ÉØ– ,³öøîYø˜¹ðħw¡,I?·áÇvtý”ÐlÊ÷èsQçFrãÔL+j]0–óXôE¼#µ\±..r;`œÍ°w,»ÙÌòÍd½1®2)ÖÌ]ü üÖ%ã/£â³7!ËwÓd¹)ÝØÂ'Òì= äg±ðo-ÏÖOíx qo8žšÚ³‡ßÝ= 1÷¯ä±hÿ°4Yëºÿ°=™ ÀhײÌé…¡FÊs˜š`̈7bîM´å!«„5ÙØqåNkô}6>xÁèmÁ9%Û!ÛÙsSPš5s*kz¹Õ¬=uq Û3¼hˆšÎÈÜÝ“Û'1¿ÎÙÍ©TÀUõ’‡á8šöl|%™º Ÿ|mGúØûnLÿóþmˆßr%ŽêÝB·bÅŠäŠÿæ•ë˜3Ú€ê([F`[ótÛ ûتøà“/¦¾›KSX£Ð “ 9àè\¦ÁxÔ3Ýœh(#rø MÿÌ0h6@žÜ¸9]²z) 嬉šŽ&€Ï~ö³éþàbŸŒŠ’Ž*õ÷‘wP?ܪñqqdu7CWqîíLŸäjà¿øÜç ¬-oÓGÿRžRãü¬î¨¾8Ø·Ó”Û•z•2× ‹~'Gkä³vk:5û‘-åï|£ˆ§O¼ §t[ó_û˜ew:‰pJ|¯}¯³ßÎòÓÓËΉæi©mhwjmoN[:RÛ¤•©çà<\Ú íÊq75í¡ãdÒh]¡sõ š~À†T q¶À£Ô„ŽÑ>Ú$3mÝèìpß¹oßÊ\@Ö|5Qà½y¹~¼ußž‡ ùë´eñÏ¥)ÿ‚>™dÙw’„ïáÏK<£Æ'Ó,#¤Êù3•g‡¦Càœ'¦†D`^‚˜Œ‚’EÍ.@spg­°Ñ úµï2~s¾»,“ôA€-N¯“ù¢,"^b„ã#߸9(ËÔ˜ È/ ì? Œúí>ûU|—QkãÝ´ÌFUé(/†s&Ɉ5¿q-Uƒ( è³×é‡aŒEJÖÅΞûIÆ“$cBð¯ú!Ód:,.Ô”õÉ´8}‚½`€Œ¿ý*\IH7<,Iw(ò—Û0‹¿ùc>›0áy‹/":ËÇÈbæÿo Qñ€ošþãj~„ùCFô“uò8ótàh¬hl8èÄU.á,€‚²ÍZºÑ®uR¥ÜΣ(>0¥ÖF¬Ú<¯Ïo Ð?GaŽÐÍÌ“ˆó'1B>p€༠+â=óŸ9 ŠQ÷4Ntr Y+ ÿŽŸ `õz>3Tºá,™˜ª=÷¾žìØŸ®¹åj¶3ž¿jÁm¶± ó1S€3y›8¿çïêe› £Ü¾ÂœŸhˆZðÙªò= ù ðÎÑk¡k<ïÙ²º^¨e7ù[彿'fEèáltX¥Ú8éÞçìOÖ³ÿç SëÓüÅÜk±§9µÏH³¸Ée÷~wX_]ÑŽ{†›ÐÒˆ;H§Ú»Œd­æ»ó¸‚×b” !ŽEî{ߊêËx¾t,‹çl6~9rw*@ñ± “f­=‹{÷&à(ÒÇ=àFÿ÷IÖ#gqŸsú´¤Î587"CœnÙÏ[*ÎöñìèU°4ïÍKÁIPÛ2ÿd°ÓÖIb/ªñøÚÁKFJ»ÙV<ݲú<´Å‰‚ ”¢¼2£ Û ŠÌ_ä}ºµpÊ8ÛßXΗ£ þ|H $É]½_&JÏÂzü~bÁv¾×1Éu$ÝÆIó€§s_Û~‡¿X¨/.®<É“ñ–»ý"”aÌÂD»’[eÌgìÆÉ–æ³}ŸvdÌo¿ëóB~¯†×å(àÆ7hÞH|dÊ 9|çyˆE ,làª`6IÃ, ‘e`ÃßaÜø¡ißß$­`5ò¡ž¶´dò¡ôÚþiZëL:°×Hý¿iKªÞL©Ùù J3Q"­º£|\ð׬JI—ýTêj À²ƒŒÎv'ÏŠ«5Ÿˆ°ƒ·œÜÑÝŽyРsúî¡øü8Ï÷,åg"šÅØûSX(€|À…gôÓñÍ<~™GŽà§¯‘Gõùô2ùÑÁ÷b;Zq;ë…jn†ù&0ƒœ!#}N­+æ¨É½vDÚ‡ÞvS3ýˆÓ^#¬t@FaP†Í4ÛÇ t‚¾}¥ý‚µ-Ç˲r”Ï”)åæœ¿» Ú4¢PჄðs@Ý'ˆÝÔÂôÍ \‡’ˆÊð#ùå\ô+#Ölz\P7’Ö£LãrTN~!)Œ)ëyuWfÉ|5­Æƒ2l$¿†ðì”ÕJtÉügÔ>õ(MºK–7"à :¶z#àé443õÀnï³`l«CC>ÇÖôV.â:úÔ 0(ãÞO,Ûº!U)·sÍ,è| |Ιéàé‚Òè–x¢ðí¥ãí%³¦Ú ›iÞW?05¯z™ßl†5hBįü·çMb”˜ ÃÉGg¯”4<þüë0sÒ ¶Tæ:eóþ­&ãŸ%%>gà÷Y².ägÓ!ù)7,r‘›šåo…ž¿ÔVß–k#¼Ò,ûW;vó"4ý*¤2afýÊ~åx˜„yÄ| sݦßós·Þ?½ª‘a•þÕÌêzfsT5b<4; ³eôè±íØ 3ì]Ž–EÓ‹ s€8tvkЗ ì v£Jеv²’±¥S ÝÑíKÂÅgê‹P„ŽñlGn%Ú!¾½‚§9ñ>ÖOãÍtÆDÁƒd¦”v,«³UŒñÖÇ{×ßÑ–¾H2Á¨È{L2CSÌfÇ<:/,ÒÉÜÌ@)¶Ÿ‹>% @2:æ“y“ÁŸÇ  çxÚ†¾+»È:GÕˆ…¸”l BÌ/™}ç§o%BtZñnžúlžyÚ m4n $¿ã;ˇòÐó×Då²0^2%–‡~Ë ˜ï¦Çr´ÊЭ"tãu=êܨa-±2Kªï1Ø‘»tg:Æ"ãóãñÁ«ƒ‡éÇÎï±Üjf>>„î¨ÿÔu<›¶Lž‡°ƒó˳:CO͘yâßðÞucÅ‘±¯“£².[ÆÑöÜ™š§¸jŸÚÏar^äe@œCC¤)kFóMæ6àq*Ræúl´2m0@Xý0Zl ”­j‚3òêú¦&û0‚˜€ŒAôcETˆ£ƒ²A ꂹwáéÀ¾}]ËV­ä|¾[9¢9´0ªm°¦Å(Ö³ 8q’yõiÅa #äTÖ8tàæt'¶ $’૸ۑ±JP–ž{q+Ìå´rñŽq,ŽßíØ¾=]|ñÅñÝ~ƒ î`4Ywàõºž`ø÷Þ{ošðßvë­éãÿx¬ØÅbÂÔ½'}à]×¥ï=ÿj¶ÓN/¨¥Ë辨lbZÃJíÙ2N(ð»ÓÆAÚÁ)‡vX+X¡¿mç^³g™¦Ý»vÅÙ2UѾ s*SR?Œ€LÑŠ+F1 ¬¤Ü¸…aäÛ÷pÖzÉH…á[ø X†UKš‰,ó*ç™F‘wÄE3)›\c¤qU:–²ÅÒMÎÀìGö?›Öýè*|3¬"fñ[Ö-íÇý5"™*ÏšjGªÅ½x=óWwõ~Ï´[g2xŠ»†¦¥Ý¬N¦j¦ûDšqj#}Oœ³Ž úvc¶¿ •ïê¶;Të  /3 8 ”‚¹ bý•}• ­âÝÕ×ëxZocýxÒœw(.n–±©Ñ÷Û% €=êé|?Ói!ÔÖÜ-„“>BrïÆýÿà&Լ܀ٙK™ Õ<H«ˆGàúƒ¥.¨TÉmw¯a€çD‚¢#VòbðžsžoGäæã<”ù'3ažÚ–ï b†c:¶é(¤ €øÃˆ³} ÚÓ¿c(óZÿµoyè§áúî¾yËÏTënÊs Û] ëÑˉi„ä âíä_ ÖïÎvâaìÜ@D¡RüB¶6ý5â…ƒH»ÌÍJÔ\ÔÌ÷¢oGWËà Î"òÊ©êð î{Ï@€ûˆºj=mCéÎú鉊R;ª @£ {ö?y7Hß©ôA0¾3Ë¢²–&Vô³µ|\(üá¶@ú§€¾ &{€š>û×37ÄÀÔ0ÆÎ>äo´í¼ X»ôE‡6.̵ìí@ÝÃC'YŒ± –‰ŒºÁ´ ŠiCDsâø1žÛÒ ÄÿǸ#  ï ‘ •nÊ…‚39²÷Ô)¸`¸¶XÈ"BAÕQ±¼£^GëRO,ÀëM¯¾(=½a[̽ïâОéåêx;ëF¤‰Ãˆ¼ò8Œ„"úçž{.$2³Nàšk® ¦Ác‡÷O}ßé÷ÿøóp„€a*0L%9ú­Z|‘žŠÆiT2NYxÀö•@d@ GêßÅ}צ'8°¯ßŽ¥ <Ú?“á€ïõ €¸²`žÔܧzOB@µÓ Nx_³$=úô‹Ñ}ùí­¦šd†üŠˆ ç;‚ á=ÇU=çŸa­¾”‘´.dÒ^ô[ªšÅka'ìVìä«Ak k~çÙ:]?"~¥Åú¨xD$ŠŸœßjHö³bï\›èÐw¥£Í{Ó²nnì——Xš„Aõ B†gî õ ”­@ä(W3ÁF]ðrjÝ<ì=!k6v£Ë8Øá ú‹ÐU†1>é³N†;õY¢w1ZKì~†ü¥?/òíW°tŸ6Ç¡`O¾@·ÿÉ4íW +}OÑ®ÿnÿü&j®-Ÿ°F¶'T’¹©’ß6‘VwÊ|Xö˜Žnjþ™7‚®ö¦t‹­|ßç3yÊ6²ä43ø^€[!!1Å–$è–á8¢?N8‚—yl™^\>+ÕÑŸ(sÔ] 7sÿZF¦ÉóV¡L—aNLÑ´Jq¿÷1Rz‡ndBƢɜ[xiê‘o¸i¼‚dnË^½ÙZâdX×L3õ4¤Sè~ÎCaV‹Ã‚L¿iRZbÈ,ˆ¡Ú7üžóѲ,'ͬ—Ú:V]+æ‚r_b ŸÒEÛ/i`! œÏN7Ö°¶µc ØzXlè±ðMÜ0Ô'ÃÆŽ+ûœZçÁŒûc°DÈÁÜúMì%kïÛ×}œhí²I¸~æ öà0¢çз\˜žpøðQ‚IiòôÉ©›»Ú9 µ3ìÓwß»' ö÷õ¦ƒÆÂ;wnèU4¿isºfÝÅé±g^  æÆ©»ÏÑ{4†IÍéÈ .%¾@å‰z®îw€àþØc%ï¸îºëÒþž©éŠwÞGoÛ¶ÊAÕƒùО½ žÉw8²aË,è§»®¼òÊXgPµë€säàa.ó¹6ýÁÿõß#Ùu×7è^)ƒT]DïÄcØJ yl² ™„Ì<Ä~ºOÉ=w§k¸ÚØ—ƒœŸpÞ(7 Rñ¶êԼ˔ù^X/¾Õ»)?fg5]ÛÚ ?Í'=Ñ,~ ]N?úÌÂ~X(˜¸pWFÂç·Œ_ŽeLA”þ–Þ†fÙÍñ­Z<‡ç}‡‡Òú«9.{Ëä4¼Pî¨w3Z æ¢ìh‘¨˜ÁN]P°³´/²þ´¡ìPí48˜¯‘mâYû»QvØ2ú'Ð-Déf>j1jbм=P±Cµsî¾ûoÉop eß–.…ŠÝÚ£«ÀŸàí“5±Wbx'ºøpΔóCä3Ÿ\¯ cãÈîetÙ©O蓚‡2Oí>³[óÐÑ6¨éQ´mä³ë.ÉÖ#DŠüt._‘u+þ9ºdôX°°9ÍS%.ָ̊9lÁK 7Ÿ57–“ ¬ˆü tÂÆÌ“õ–ñlhÏi‡«Ñ× ÎNQMÅHªB\S#.ß¡»µ¨W|¨£)é1i½ ûéeLž½sn»L«å¸ý ªÈ‡bêäzÞ‰DL=˜6ÅùlÔ3+}§Ó”ÓÑe\J°ì f¢ØÉá³edžÉP°¥¼E‰áðÇS|ÍŒö탣¯ñ’9ˆri(„¦†~$ÇHÀ›fFþ½Ü°9Ø‹¤­œ4•¢Û©™Dˆ~fŒ©¶‚âÃyþ5wÞ4Й;C#d¸À¯¯Sü£Yóg¥Ý{2Jfž†íÓ¦MèÚâd@E5Šû´ë°ª3_÷ë=®÷0 Wí;R—<Êñѧ_N?ö®kÒÿÕ×bt/0Z 2@EMfZÁ0dŒc~ Î¥?ù䓱}ÐsÌlK[÷ÎôS?uoÜFèaBŠäfÃWϽþœŒ›#Áv:î±Á×_}Ä¥ºPÉÃF-^rñÒˆ{õÇ8 äú«äÀ4êvT^¶µp†‚#º¦CôÏïõÔÅÁGì9LyÏué7þ.'À´;Òi{ jÜ¢G¨Lc}œëü©æo~7úYúþ^Ìék^†ç»nrÞ˜CvÅ9§t^ó³Çp“#V §êWþœu¼Šø„ž ÿ苦OKÛp­ue¸}Jš3x2íctRŒ_µ3µة٠XvºÄÀ}ÿÀSàȨE9ÚdÌ™C;0ÜdŒˆŠ‘¬[ÎV ®àûJÔ›§¼nZ;0Æï©èÅI€êãÓq€8ç߉ÿHtž! ¯b_Ð:+y`Ì‘gÈÎB4/àpZÛôânGÑ‚ϸÅO°1Ov Ü¿8µ’7€Íи™+1]Ìs÷§#vf99v`8s¾X@Íj€ÙP*ÐLyô9Ò‡óQséG1È¿†.êlNɼ½JÛy1n2–ŸŒÃ›£á7pù2ÑýTvGúƤ¹£F¢ÛÐwµ„Aùø €’# F)@ÜØ¥·  ¸î±ãÍ™è³ûû†÷†¿£œ±?uï°[“Û9óH7@çè¿+Í›?1Œó9®æwô? P”›N©££#íÙ³‡5üÃ"9ýµ@T¯néH÷|ð“aÏytÑû®P`£¤'30ÈvA¿;¯_ê¾ ’Øã?žvîLûÄÏ¿ÖtÛm·…äaÑ¢EîIÄû†ïè\‰H^l ¹¬5ìÈܸ8ªÏäB“1ýö»o>C¼yFšŒ¯ ŒÒˆ*óPøÑ/´3PZÀÙ 03ÆÃxWI)Ç®¹u=~ Cõû[õ,sUÿj ¤Ë|þ–vLw•r#«šç²ÖhõÙAù þô¥o*Í´›ëHÕIYL…_¥›ì6Â/ã›ÝŒ ³fX<VNG¸­ûþf_;û8 náÑôu<À%XSN¤¶Ë‘“ýlLةגëF¦¾™TÁ9äÆÊNÖŽÔQ§kÑŽìLôFùÆŽ;`:Èè„Ë<Û,E̪Æw;§-7 ¨ýïF#HðÍ'¤I„üӜƚÚ?J±€«}à×"ŒC®ÐßÈ7û]ÁõRtÒ "èláÙ°E§MÜ2gþ™vóBwúá‘´Ž>m¿³Q«y¾éÅ4Ì»x?„ ·ò.3abÍWJ|¨lÓ/ ð’9໑!óí"”~Z’`¶ }Ê‘üôå¨õ¨z’™1n¶iú—HSº´ ·‹ÑkÑh:ºe3å —nϱ!ÞcRqÿ`÷}Äö’ó©Ü1–Ew“x0’ùzrÿfaÉlÊ”¾„N!Fa¦‚á2-‚¼éò›àoýôÙé7ÞH_ýêWã¤<§j…Á·g^Øœr:A:ßîHúˆ‘~8¶Òã0Ž¥n_»êÚWT/ÓàêýŽ]{p¶/=úÒì˃ð<vJÂ{\ˆ¨?vþÓY¸hÅìc¾$€ëŸß« .P?ôÔKé·[.I ™Ë?Î}™ôÏi†<…°råÊ:TÓé¼øäòæÁv 82ÆÙu™d<žyAà=©•ù¯óE޼-ó  ¢ê%Õ#VͰos:³> LëýÔ¿ìö<Œ Ào¼‡¤§ò=¼!:Æ(@R”1Îáj½øî“öŠ÷â¥L[½¹ï9Žañ‡ÿiŸÖ’vXœ®œw0¥¾ôµ2·ylÈHªHi@f´¥ê™O@޶Œ— ~)º«æŽÆ.B_Œ²ó¬2Àï$6S» Á:¢.°øl§£þ A xíÂ?UN‡°>ò]:Ö­øp5±x¹ý³èäÇð ft cBã?ݼ?6ãß‹ø'æŽIn»3]U,:Q¦ßt Àö¦]P|^E§E(ý1¸#ј2pNºX­¾Ÿw#à(QÀƒa"1;µàTÜ€»›Ì•#Zû¬×ЀÒ^Þµ±‚·Kx¿UOo` (O{'™ñÄô„–ƒýŠþÏâ»å<ç³SÃuä)]“"}A½íÝX»Ç¢ù1–?õ{T­-c}ÏfWDÊò[¡Ë OÓlÜ 0Hð·Þ¹¨q ºå‘ëØÑò›åbk—]äÖËò}yìXÖw%.Ö÷~òÏ©jlÄ~;Uì!k.lä´¿VnÎ`À Rg£ÓÀUÁÃÌõË(7 …nc$ê@ `"TnlæT@WJÆŽ¼„LÅ(*û«ÜÆcþ¿´€ÀJ’9ÁQÎÎç‹5þmAl‰ØUvˆÊÚ™sé:Å ÌxgÌž‘º`Ç^@SQ|£ôædš˜‹±jCЇ覵ÍMfd¬(ÞrNÚvðÒÓ/Zc›XXèâA¯n Àg)ΈfO| ‚•Åu´`«]uEîŠî]بXtxGZ¾hzúʃ߉ˆõ»²ßE€·ß~{z?7º£@¦áx,D´¢Fp5 ƒ’…ê¿îØe㘞Ö_¾2½¶ÍJ\Ì£yO%tšA³\é´¥4Ä .â™ô8-áwEVi€©“_ïLÿ’Îná¼™i×ÞÎQþTíþ(Ÿ•Á”ò£®³©/íÔÜL™ì6ëº-)Üó¬žÃå'æÖÃÅ·§e硇۪‰á„»ÂßHß3£S¦wÙ¬êü‡}^ôÎL},,>A;8²}Wg#ÒL}s¨æ€Á€£]—‹Ð}Vlº]P¤£¬‘õæ8ºvë*^±f‚§˜ZÀ³Ë~L%ÍBOІ]\6ôta§éFž&4_ÐÜN([éÉn±Ò~|\‹0œžÅÂ1ïßõ9 nBðË/òR³ Ý™ï¶ ASæ\æ†ü‹sö×¢›>SïˆZð0_L·ö%»Óõ(AÉœ¶wqž4‹Ðí—œ¯&Ñ &–o½3’•‚9±†¸n5ºq™‚²½c?H3™-kÀoÛQ–CŽ7Áxè§L†e-“ñ=tÚ~¬ ˆLæýLوٻðíõâ[ÁèÏX´(„öM—ñì:·Û•TÀ­‘ž“P0?¦Ï4LC-À|~|/ÒiÜÍsïg°þu ›fž]_áaJ<¦s«[Ó¬Ôd:º‘³®Î¼ÿ3úÆù7Ї77»þÊí}ô£ÑÎñFTÁž‡Ê1½Ìù#HpÜÌ!A”$ø‚¸s£ÖÞÃôÌK4Ú;þÃêüPÈ© ÎtrÞL̵·1ÒIg 5ÂÒŠ(Êt‚ñtü56Mн™#pÑCåÕŒ“=D‡ÎÜ ¼†cóêFçö]ÐY%ÛÅ{ùVêíµÂ5qþý%±[@ÆÂƒ~òw9ÌcO Než:ò˜e*¼8È5ž ©—Å‚~>½ç¶+Òç¾òx€ü³Ï>£~ϸ•mƒï~÷»cLIÓ€\ïI¦ØÉ*¤Ì‰ " Wé·:o¶v5ó”ÖQ3 ìU’ •(¨“ÜîÅ+lG‘Dpp€«ª^}¬ûn',Ž'n&OVqX;ʼòû[EÆ5€’„ÅZ€j@&¶¤š2ñ@ëõ°®œQYçC5_´W[ŽŠïîÍâ°: §3?ó†uݨršx–ªáú½êe-žaó‡ûéŸÌ¨† ©¸ ‡}që.áüˆW鑽aÇ=åtÎÕ7­9ª¼¼ ÈŽN‘v'º#F뤱s„egl§«™¯£cÍ™ MF·NY/•¬BàÞ¹  /6·ÿo»)»ÿ0Ïå—1ôýàÍPÌ w Hý&~Èœ3)Í„ö ì\Y.À¿2·!:âþ¾ »uëÖ8fø®»îJý'ìT¦â× Î;`N‘Jgø2‚x•Nvu§£›¾ŸÞuËéÿü Nì±Ó¢D‚fR ÅùÛ·o÷U·Ö³KlžŒÈìŽk2xjßÊ]ˆ<§[®¿,ýÍýß9/ @í$À:ÐŒFXo# ÓiM¦Ÿç À¾rW¾kW;Ù^~WgüÅ „;X¢ëæÜyŒ0 #Ú²ßõ[;è†ïº‡F}Ñžîêi<óz{åû+Çf¥Q'ŽœJ+æJ_c‡Ì¢®ÔÍÁ@ÅŠèÅØh‘>nï.°¸Î•ã³xöÖè¬àr © T±øíbôÉ(» ÁG°±“– øtj¼×SÿÄN¨ÈQ¹T|õ{')¹%õ|ƒX~‡à¦ç ÈïŒìe±î¥“wD¤·`Zæ“VÁÙ´$ôæ¡L}…`sež¸˜R@0í‚­ $ ¶úá(ºy-ˆ ^2N·~-çû•˜÷òL¾c¡¶uO!º@t¿³ß<Ü™2¶yý9„Òý5ž‚§e"æò,õs éšt~Ï…É{~#Þ³Ñ úÁ8Î\ûÝmÄŽä6]<1Y׮ƺëôÁº¨^0Åî†ýT·Mx “àÚ /¨š]Ú5ŸÌ Ëö8Ý|´.jæ­€–‹öô{¶l5Yÿñ’>Ó­}Œì¸ø‡œž‚T¹ÛÕ‚>ý ØãµÂ^øÓȺõÞž^ža¤]ùï@â58d‘³eàW¿Õú"@øem/Ü^¨_kÍÛ‚š[[)}tkŽW÷ þ÷ÆOøè @:…è¿rk +1ùëéé‹[™çöÆÀž.ôÔuÀ±ÂãL.zû§'6°¿þªôò矵ðN;Ââ,ùüyRéÙÑËØ1GñüË[ÓÇ>sáp/Û­h2î>¸ï¾ûâ´ÁeË–Å=—]v9¡´§ô'ÒÆ—^¾ñÔOGÞÕéŠ~ØNGäï¾m=ÌryHLÍ"¦! Ç©‹oûÛåDªSÑ=BÙ½±qrø-3㚈˜NáY2 ìǤïy%]³~5ÒjªýÇ[LCÄ=Ó(ðÌ X-ËòÙò ÀÕ!å` €÷=«¸Ë‹ kn°fžÖ+ _Åõ v* ßyäSA> üá–zùµV÷jašc f$¼Ó¼î{øS±ŸýH¿íâ©g× iÏI$Í“R;mco'_ Àå¢=îÊ-N¨³ƒ\^zeG©8öû(ì8}'v‡–c÷bÞWWÁÅŽÚŽÔ#°5cï7Áî‡'çšÀSõAßýiÁ¤ôðYþÞ˜ÅÉy­×”.¶’·^òdù|݆¶ œ)þÓ2Ùγîkq{#ïÓóîÔãYì_œq X‰ï®Ë¿†‰}Lz*à˜ Gù"œ ìaAQÞ‚€¸½†oWb~‡këD7»·íyÆéú9Ćã˜îL§ysíÝú^W¾cÌ¢B˜ï.ʺeU¸xgR·¤oÒHÚ…l+qey˜¶ch'2 £®Ç´]ÍÃ\6O³)ËÍry•¼¾É³aAA ä–©í=n´þ¬GÇYvruì£OS~ƒX¨3–ÃÿofÇÓÜåfä8–Œ0CǾ(³q–\Rv˜œ ±LàEuÊo× ¬Ïx:§Šñ¨p!¾1Öšð÷]åõ¾'„ØS2X^WÈÅ:À ?Ë€ÄÿPZ²ˆuM¤ÿOöœN‹æuÌ,ÄT"3r˜é$ƒ^ Û9dÁW^AFÀ­oUõøÓRô4J† œgöºqò·tá<à“á_"Âï²ÛK ‚*ˆJ0¨>|óUéµ×aËâÇmzºÑiÒ¿ö+–9ƒ˜“¶ ¸íµí!XèNüã ®‚„€Òý.;l~͎ݳy/ëQ™/[Çl{6¸ÎÎØšÒsÏ=þä0xïA5í†c»öÄÍÜtÉ¢…ì>xïÛipȇ'8†²³”ÚVàÌ_ŽúÔ¾|nØ ´jÂh(ß+J¿Ù½¦å¦©*ZšQ¼§ˆ{Û¡<ÿÂe|àÉ4LS~qù]wÓÒ¡õ{¡VÍN§˜¥,\v8-à`’Ú%éÈ™=à #ׄl7ÑÚö/E;è:{ó´¬ë嘠î U°ß…vk[[ƒV°J8PT‚”íÊ™Ú;S.xçúʸϥnH\Ú»&Hš+ªoõ@¥â‹}þJÝyC)x0Êd"Ýݰ-œÉ+4¹3µù€¢,y¨†3n,Á´%hRN-Ø{aЈ„D—à­?¤ùk¯S¬‚jÈúº“‡¥hëÃzqùÀïÎjÌEhëÓ4XGYˆÑgû¯áZÏŽGËÐÊ<\&?çUÍwC§l"•sµþ{¸gTkŠ-ƒFyA*·Óiþ%~cn¸ ß·À¶±-aþ£:!и3η^‰i˜‚ßëÇygÔÉ;]rBnÌ8Õ0l=0¹ŠSy‚Ë[ã|}b¹ wãp 8 8® E¶lœ£*û ‡-† osþÆ9•ýÚxÁøÀq6#²ÕÞwu1q`;Rök&ê*ºO{l{ö¾÷ôÂv§";Ù?ôôPêbM§Žnc½“«Ó©ÓÊ èG%ö!4';Ýûæ³’ ŸËÍvýžÆQq騼¶)³yKßæ± u¢„} AÜ™¿ïU€’ô©ç´iãe… o:ÚêL×wÙK¶†;Ì‹`ï–Bo4Ü+®¸"uwwÇñÀ~;x`îGÓ}¾âáw 3švÍ™TTÈQ$.¹øÖ·¾;ä¸4‘ó£?Ã3Œm»íͧÓÒ%ÕAq¦ß; 2e3¥ÓðÝ´…Ò]ùv•÷Ão·¹Œr>5µËõ‘ß ;/1„[Ü£©ÒŒ`1CåðyÉßË/á0ç$Ân|(rzâpªñOsú–^—Ìg…~OÚ¾saz³gAš?ï•4ŽðÒþ˜õ F k Ö6:ÝnLµ#‘çe>‚¾ÏÎÒr[D$,ÆuÜ¢~à\ûÊÍ­%8 Jo]Õð&3lø1š(8à™þ郷ðóGçìµÔzíO¥¶{œ÷0x¯#ÅÏbB³Ÿ0dq=îäûä“lç['_ã @(Öð-Çó}m™1c·ÜVYF‚0Ší~A\Ä£ö'‰AÙ„ ^Ê1ÑùMCru¿ n;Ñö^ßW¢Cëg/ÚwÔ™îsÅs\5,ø¹3Cº6/— !>.@Õ ¨”ÉðXf'ÌÝq63ˆy–²îIÁËÁ£ü/L­ÅÙZs\*ÛØ6´¦“€hÛ˜ù´œm›ra$ÇÝÚÇÞ|Ê0ìZ(·ö» cY¾·1I”*v ,g—Ìaúñ~úœ².óÙ/³dLPÚ߉£»üÇ8Oº­u>còeœ/3:BZOof;¸‚lÅ£FGù™° ÞZ?ßÁ‘¶÷ùçb"¸Ðgtÿœ¹EZ.>±ê’F)ð9ìÕ÷²šNÎæoek íÖVNæ£ÒF†FÁphxN tØe7€•åá;Uå¬×}ùUÅü9õ3ã½vêÔË€Y›Ðé AÁvÏ3lgý¨û8k®nÙó>‚'y!ÝuÛu,G47Öã§„Kø—.²±BeÆÑcGƒ!1!§bÛ¶m±Ð»¼¸gݺu!áoã¾ãækÓ~ý»‘O‰ AE07].LWº©q€Ð®ç_M¯lÙB9÷û_÷UâÅpÌcO¿Ô‰ô¥Ÿø`zø‡›Ùa'|ï”ñªUš3m¯kÄ®;A¿ª|Ïöù›ï†U!&rzm<—î S»†}¾©26‡?Íê÷"ÅXj_a6Üåt•ß²‘Ýú^ /;汓m©c~WZ~Ùátdg{Úypy†¸-Nê»’ µu˜²õ­OÁüELÌwAFà4ÌÚÙuT‰ÁÆáR`²4Žó,øå;ÝÄú²Ã‰„€ûÏ×cJ8œ_ ÖMŒù]ÿ-ýíóEÌ)ý»óx,ˆÓ®ŸeNü÷äàaÂSϽ à<ÁNù|œ7Ë@ 2ÿO”ï–Ïr´ nù¨µ—(°ÿ¸t¦ ¢ýÑ0@Õ˜ñZû–›ò{ùî@Ltólù[/;Â,ÊYж¼{ø.¡`º[C¢Ã¸¬”ëß„5±‚ïë0WcªM{®›óõ–ßMeœdzÈR[C„`¦(Ë€ÿ)9¶)½-%7åq|ö£M§\Qóf9;ž›~9OæunI\,Ǹ#»ÑOóÞ…¶ ërm~ú‡Ä“×qëÅúÁë£ -ï1Yä‚Vºõ7£Ò7 •ß«î«ßËç™:èôÙ·1ª%"ó“§Müæps\¦™çFjø®»PÙM~oXWÜOûöN^tK«õ¦Æ~h7÷^¬<“öí^AÁ%¤‹™OÌ)Á^­bФkÈÇùÝ\9`:û²mgBÁ0™!!¸ÉzÞiÎ@»%L~Ϋš.£Êo ß^¹ =©€Ï¥ž&®_Í`„ìjwŒm™Ú<ÎÂÙßva+"ò'pXFí2ÀEbH¾ Ó¾«AÛò°Ü4-3öÖðL?вu¬ggRÌbOòìäO 'ólìÑ2V¢½œºßݵìö×pƒª1Ë#>¤¦€[›e.`:›•P¸sm|%&W—Ç,Ø<§Ä‹7ãÝŠÉ {å±Ìm·Ž–O’¿Ó|ÙF¤Åß\™zþ9#ÙŸ¼7¦99竜‹½¸0}¸ÄÐ6ôÕ¤s-¦å6†Þ‰»Í˜r„,߯b~­ŸC|O´¹c,¯ŒYG– Taä|uÓ²'XþJó75ÏáYA>ž©ã&¸¨cŽÅôkæFŒË(GÕÁpË`Ü ñབྷšþDÆ,* Š'd"€¡á4ÎMøû®.*€RÞßËþv·ÿ‹Ž¡ÃÌHß…cÍŠ /3¥ÅxµWÍô½øò–~—»MiÍXZ±ãhê¸s0õ¼ÄYr—J[ÎÈep 0ü.ÍY¢lêe˜Kј³z''‚Ž#¹Àgµ0¨F ÒÝ´¶°ö²Ë K04Ž—Ñ~™ÏÁpËØèã“hÔ(ô‡`Þ¶Ü7Ów.å O,O<ßGÿÝèmÍBwŠýÃ|˜7ÁQó^ò£àž„Ï´íÄq[–ºå"È:‹÷»DƒvWa^Ž™•å¹—Ãhæ*´„¢ñR3\Xæ†zi:N!P†m{Iàîº62ÅIIÊp(Yø3­ú±…šÓd=J€lÃD;#–H¹7$º;?øã(êû#¢%é—}ñVl&så÷¬N§S­ýk‘ô–R‡Oá’¤ŸOÕâGO›´m ôƶK Ö%¼Ù÷…}Á1ô»ÑæGزì·bšGýAÀŽZþ¶EÙögš)ÏP½\”ŘG2 è²ô™õsÃlÁØö‹«}Yfv%Wÿ.ÓÊIn†Põ’ Æl¯ ‡AøÔßJÿžVä[@IDAT>ÉÐáÈаTžå}W0:4plltbäÔ™Z}>õ\gf `Ý5gÌÍ€ÞÇÌìuáÂ4Öùû‡9Á‰ê§ž{z¹¤K&ÆFBº³¿Ÿ w&[Q« êi~Y Ï£|,¿‚þ{i¸Ì@ï‹Ø×¼ 3z“½Ô Ô~ eù{°ÒýÇ0%ªýøSLCD:˜= ʺ“k— Û]Ç{lø§È§¹PÎü [±+Iýï|ç;Ø?þxº|eÀ'¡ÆÒ¦M›bÇ€ùR¢ß|9³pÊb¡+,9yJb"Â91]U•ó÷ƒÇŸO_»ù&ÒQýúÞ<›^Yr-b”‹zz"f¿Ù¹¦«év¹j¢õQ­Ãö=â¡NãÙ0K·á^¿áÓ(_UÚgÿ¥{Ë·±=0§±üfU•ƒ‰pªÞáó8WÌŽŸ`çÉç×sQVËœ¦´ë G1AËJœ(ó¬HA†%€žQ”_ÌllEåuhÓ¯? Û«sÓvs ÷ÎÚ/Ì‘-+XJ4^6œÙÕIóÚÙÆí&°®ý˜à¾¡ÄÃ3³{.¿8Sm½?«ö oøq@èYêCì8¯’»¡CÃ| .ÎF%>䘢GÑÝh‰Áe3úÚ²qÍx-æ&´eDŸÀdM[îÌÖc–.@`¶Ü=ÙN`²œÀºq¢š‰¯làVÀÔw%A,ÄRö7€•— ˜Å2# ‚ XfÁ-e_poÌÅÞ­wÓ#7  ^Ü2W, æùÕ8ôIó5äà2Ý2ÈΨZ)¯Å=âíø.â·ˆp~nNÀ!yPåäñi+07 å°ÌÇ~?Ïb¤mÖ¥+µUØF‰$Ú¢mͼØþh<‘ïÌe‘°•PR`R Pí2 Õ\=/Ãâ{½…æ˜é;& ;Ù«á S-)ãLÙ[8s¥¥Îà« ¿ÃMLáJ7c?;f&Exá6>Û$ƒ ›Ê·÷]ý0¼_xÇÆFߤ"FÇÆkõ“=¬<Òw-àÔ»þ3©“ãx{ÙVçíLgxo[9Ü¦Æ–Žž8¡‰Õ±aöùòox#¼×ØË$8{wÆ[õ:3_±ŠÎ<ðf\&Te‰ÇõÄ×Gø C umø'à*Ì#PUGٹϤë®^•¶íí ¢cúìܧº—Ó°Ž3ü8Êx:Dƒ[ ¿ô¥/¥x ˆÙüÏ<û æ{à›éŠ+¯ ù€ 6ÄÒÆ§>õ©X6ðœ*!à±—û“®²Ù™oÁ¾Z¦ÀüÏ7ðdúÚ÷Ó\NÈ:jvÞc5ÁGp“¾"¹î‚ÕF9Gg¬U~³œ° ûìÇ׆â{¸Á­îòsÁºk¸j´‘žfñ}µð››Ó7-þFuþË4N÷Ç{¤¥’¦/»{æƒóÓü9£iþ(;8îyx.fÍõd.;Û@1{ß½€î$ÄY¼³SgðËxvPÕ];ÚòÖÓ@À­Xl®à]€),„­l$Wñ]@œÚ'°8KÕÖ‚ø` ?‰ï5`!¸W¿N§Îr?Õb+©ü«4qÙç¤F×1 ømàõ¯¦º:ûMP?…–˜\>€)(K ô`¾€©`žÀm$ ŠïEy¨«øÕÂ1Ìíø9ˆùz.ZÀÔq J†kY:+µÜ,SžÛN“i›oƒËÁmâtñ=f醡Œô~8›`wl ,ëJð³>-/ã³Þ$`4GД˜³® Ï´ø]%¡`YÆTÕörÿ+´«:}~êÇÆ[kú>ÓoÛ>FÒiNÃb¾Ñp0Ãå±û•äúW1ms¦Ë€Q¸°užG¼˜‚­¾0™ã„@þF ?ÁÌ¿™µÿQÆH›™¸Åq„8E9N K¦(­±‚8g Mñó¿Puì뛘ëEJ“š§ ûaI#˜1oN{t«®ùPˆã£étO/ró|dô:GXú°®›dÍ ¥yì$„,Ñ3®#T”¬r+E¾Ë*M'Nõ¥¯^Ý¿ëBY9C—PZß­wj/ªª1×Üi€?yÏméåû˜eO'ÜNÞb+CÄ£'³2Ne¼FXsݺuáßg‰ŽíxÙÑG‡Î¤|0– î½÷ÞtóÍ7‡V.áµ×^‹ ‡<Øôµ¹…Áîä)ÎQv¢ëÃÊ’ w¢ª2‘´{ï ¬W¤îÕËÓîýÇÎnÌUOïð9Îj(;M~¹-Áš+íI@Ä}¨Ò±߆ Ÿnó7ÌL(LIn cŠÒ-¹»V]üƒÂõ7•¸"ž áÞY¿ ß0ªádoïԜϬðä©Å©u>Û”šúRˢ݌s%4í-‚G~¦8 ÅWÀ$~i%ÚÖ¶ (›³`d?ÚB!Uç] Ñd°ŒR»ž_ÃX…>?ðã(”¬â zúè«$u`s+}" %Øã{JW³IÕíñ±÷ÿÎÿ#ëgs[µ·ÿ:Ue~¡% n˜aJ¨ìû–I&–´³?9‹ßy­MwGД'ÑÁfŽýþ±ïœg—)]Žs}¿f?X¼aÀS€/fÀ‚±u"ð ÖÖ¡Ë Wb:S¶|½ŸÁôì@oG}z2ö]¿ÏÃÏÖ¥~ÍŸ3ï×ÏÙµé5Ü©ªþ9ȇOQÃWXÎÇç§:h¼±Vγ;€Lú{ž_k|œåÁYþÓ¾¹T"°3²¿„gëÄž"øË™²NLë&ÞcÚ.ÍÛõ¿“gpµ•q |ReçÖÌÞ½öº‰K|ZÛb¹ÔuÿØYÁ$²{•¼u1ÊX,w¥•eÎ5j\W96ÚD‚a³%½•±½ðà@ojcøÎ%OSU‡JÛ¼þï+8TvÈ©^Þ·‹Šêµ×œ¦ÚmTÌÉLÇNpØ •81 '`÷–ÀvxÐŽ'3µzrS¬×Óì™ÙvàF+3¡æÖÎ46ì@Eã¤VpOïShÏÛ™²€þøžL×lº9Öó½ÙOe󃪈¥·ŠŒQ©²ÛóqÀÏ?ÿ|ìÏt«þ0ÃÑç·¤ëØ èšºà?Õî¹ì^P´”û)”ḠÀ8¼1Ðm‹Uâáè Óv<}èÆËÒ×x"Ü<öØcé–[n b`ýúõqé\Lœ!ªVÖ¼ÆÉ‘FzÍ‹§VUæ ô³óÁbÍŠK)§—¸Zuûn=›g—4¬ëÈrWÇ3v¹lýnBÂ,#ðŸžÝÍ $ùì?‡‹E#Žª7Áß2aà7ýf.BößI—*Ûfø-íý–ÃÒÌÏÚ¿ªÞ6œ.]º7½q[¬V,OK:ö¦ý –²;]ŽzjÝZ¢[Ša1XÆ`º™4íÁê ‰£]öË>Ø7ñ]º‡«€·—g•~.P“÷-*å<§í§þ‹^4ý-¡ ž'¤û|W¤žEkÞM’ÅÅ·­ô¼‰è­…[_Û†wŽ0>ÝK:­r/¸(&šÎ½0í=½–Û.®¾,5ýÕCÌqò`ÒHÉ»ú`ÙUÁÓç ÔQæ[U5­ \I~ËÏ¥™Ã¨†[†2Wx³g*>‡ƒ•œše¸Êaó-»5Ì”ð³Ç"èø ·•ð+ŸÞÑc/ܯ¡úPê^ÀV׿¶´y§7;\ÎÜ%òV¡|•3¦Çy¥]41¨:KêuúÜ{$ðU¸™‡–p,¶¥,fGy%Õáf)ÏÎ"e£ b‚è[NšoaX½ÞÃ/’ °Rœ`z®öf) ¤ò½ÿšò§¹z|­˜8nsÛÊÎ8§Ôæ/«½¤m/§ÑÎF| ¢=˜€m+”Ì0eD™”¦ÿ÷$µç_’Üö³¼üßèçf$¶¶ÓÿYb8{ìN/øÓ³äÏYô1´ãý?À_ VHP"Õ2rMþjžhÇAØ™º2E®ï»ì2¶w–@&q`x†a]XÞú÷6óÁ¶7<µ}Î0÷ «Bâ‘cãéŽç0„Ç3œ˜áM¸E@pdu˜Eœ<žGɾ}¸pÄ„>¯V'8ñ/Íí.>žõ»$H™Ñí¸c™¸`h;Ahö³Å˜ êQBf!ÚÞ÷æ.´å —i¦Ü’½¥½eæú¿íÍö'Qpdjm„íéãA7b϶q–jMÈ>P̆.×Õ¾(>(8¨…»<ùϾYkâ:îp«Ÿ»ÉƘSά.óR9Õcìx+а‡À 3Q¨‹Ýµäà\ªMH^¶·ÏI}gN° Àì[Ö9Ò™­—Âís0½ ‡Š„@háòžó*» p®* €uëÖEåõ«_Mßüæ7cÍýR¶ÑI½o\Ç $qÛ‚v ùó ÒË´²²åI×Ð~Ÿi—TºÂêªËW˜K$T•ïmmPõÇÙÑ0üù;;wK KY°Wõ‘'·¤ÿâ¿ÿoØñpÃP Ü‚¨@nº$Ô^C¼'¥ÿ5õ8L˜KÓ½÷.KW_}u¸ó4C·5êÏÀkÙÓA^Úº3mºÿóA\E$ïññ3}±zÊѲÔ^åsõ]ûéïá°ü‰oúCWÃÈn´Ë„†nTÆ]<¿EÌÅ7"+¦ÆSU‘‡Ò¢úÅç¯ÌÓtÕ0ÞêóÖCméÞGÓÖíõÔCI\SÝI›kÌP£L¹™‰ìúuX8Ð:ûtˆ$&Û/(e]8p1øº±kCÛž¡ª ïí©8àʆ1½õÖ‘ ,žKI  pÁû_\Jˆ»ŽœËÏôob´ËÝè£hAßxë,˜fÿ’-/ÁcQ¶Qfü¢£DÄÁiL¿J̼£L O»Ã¥ì-+ãX€>‰VI˜‰(ãà‰­ ŒÃòÆt};ŽvæÛP7.Ö`/[ÿÌ¢“®ÙÀši9 ×ìlôÕÞ)Ë¢K ´ª èˆþÚsg@VƒœC ú…/Þ—^<à9ÓS‹Ú;¡—qpm)#ãE˜Ù¯…€¼f͚฼šI{ùÕÝ8]Ì5ÈóÒ‘£Åï÷œ/Ó/‘ Õò©žzäÁt핞*è×_}úЇ>…ånÄÖ@2üÃg^M_e ’FÎù€ÞƒŸ¼`m; : O<;v>—ªQ?ež}Ÿž¾ì¾4ÊÑ Â_nØéÏÏS„é`a™ûR|ó¥ðkx~¬ÚÇG~vzÎî²¿Š™Ý¿SsñÊ¡ôÌ®°Ó1ûïN'>—޼ÁÝ¿” ] °‚żKð B1Âc2ê9hÚ/g_²l/ÇÌJ÷œgË·mÖhšêöŸ&•_)‚iJòà8y.Uôµ®¯1æó8º·„5þÒ¹üåoë»ÑäZô*´qJÔ肱€m^dÁ_P±LT{Ñ‚åæ2À"Lê:ÀK¹ ís‰,ßs¹ÑnBéÞ%'„¦Ãøæ¡-ÒRc i‡0¥.F(÷ñµ„¥°¢x5z:Á†U¨]üÊÖ>Ž)AûáÂú­ü’ÍZHž] ħ¶Û€ío“{è“ QÅ ‹ûqª*,Ú`+¦À/¤0䢎< Þ‡¶½ZörC¬;…V_Á ãßþF-7Û#\ª:öõáò“:Z¤ÛdÅnÆôïŽaýß‘ÅCÏÚ:Ú™ÑSWd8ì zƹij‚C‚Úà$;!óìyª­Æ}*ŽÝî&d3`16l¨Ü×}‰A£°v$1\ ÙÈŠ‚ ›÷÷g**½¿i‰ØGFÞˆf]”Wعåe”YMל…¬áaŸ?ls¨¹&ÎîF"ÓîTœÎ„Ä}+Gub1Øw[Îk†#PU²×ñ:‹„]îžw@6øã/L?ýÙ¥_þGÿ ¡Z‹ƒuj;'ÇÆ û¼¦/ X+XØôñÆ:OD{ï]7¥­ßØ GÂwRyçt/wJrêAA•N~Š™¼n˜‡š" ³#ÇÌÛÛç¥ÚŽIb¤z'îL›\‰¦•‚+—GŽ¥—¶¿\9·ß~{Ü<è탾oáž9æC¢ãÙ— œ!`ùÒùéð1f•F¾‹?1ó&|‰Ÿ¦iQUŸK‹†=ã žì¶ú=ƒ•pâ;?¦f|.¸ Å·²Ãò¡ôŽrº 7å¯,—lbÛJÃß³Â9š1¬ìæm˜.w-箌£u‰?`åÈ*úÄ›RqX`¿–´Ù"Ûå i[tü欌> úÄâCåçùÚFøá:´aœ_5­%dÆö&ñ ¼ò`ŸZ°{6e N]ˆ…¡˜9qd6?U{ļ­#½ë1eÉ»Ö ``Ú_ZÁ}!ß$ D£h‘N€w=š ›^eÞÄNN‰ö–åž‘QÿÎh·cú±²)"yj¦šð´y ð0é°,»Ñž0hzÂÙûÝc‰f,ëB÷{Ñ ÑŽ¹NyœE5ßÁ˜w=úÚ¥S„±k×P^ŒC#¯Á!$6©ó*ëâVBó¢ ¢å)&ZöûÑÝ´¸/cšVËNÁÓÜ^|o*¶`RvÌøc@á4á_зíN@ºþÊ_°°NXO¿cñ˜Ic_¯õíØYÝí^¦ÆYm°þÇäÐ#˜ìI4Ô B0]âƒÃJ& tÆXâ᪠B¨(ÇUÙßs¯ð÷“ÛrƒÐõûª&{ÑûšŒÉȇûŽŽFœÇI‰Ù9…f*a`à4‡õ,¡z9k›ºÇ-•ƒ{ÿ›©Hwæ]ÖiâHGšQsÁÃjDàlÝY¿³ß#Gޏºn.x<óÂŽtÿç> Õ×ÐDFj™›NœbDµöPÎTm™-ï³BxÓ /ÜÑO×å×öVŽ-¬‰£:ð+€ÒËù îT”ÀíQÃrvíÚaWýõsüqÙ™î¹mcúñ3/E¸ö”ÝexÐI¤Ùâ¤@éO½,¸£Á-Œ ÞsÏ=!é „rA¼+ABbÑâ%©‡sŽo¸îò´eǾ‚(ŠÞýó¬Ê¦ÏA hoÇ"͹CÐWÊrúVÀjYN¨ª- •Ýjjc“F‹f¼cQ¦3ÜâÇp"Œ2<›Ö~¼k…ÖOÎÇô¶à§w¢NëO¸…!éå¶4¾êTZÍa¼"1FìÐmsî¥Ë"(ˆÏ9˜¦A&º[%ÝÙð4ï¶S—œ¬[ËA­à©e*qêw'Ï.k,ä¼?•µÍ­ÄÎ $ì,/kÌ:ô,1†Ž"'¦°_C™&¸O¦®€_Ìþ|&wž1A†š™X*Ln^H×Ñ9Á3ØÉ1p uúš‡ãB D‰²VLA0x|@TÎÅ£Þ^¯}Óß{êðÉùKV÷Ô&d ª¸)ÉÁ>¨Ù9$—BöøÅÁ¡Sœµ¿€“¢k§ò `g ‡ÿt›‡3¬ÿŠ •~wÆîÌÝ}ð‚µö ±³¡]wÕº´kï¡ÔJãp8ÎL8ù¥û<ÛÖŸ3iÞqYA S {öÀÓ¦gA<¯uÛÐ<€¢Àq{Ñ4¥<@âRƒqIÈ©P¹…ðõûÓÍ?ù“©M‰TŽ=6º«¤nÍ.\På¡#6èÑ8kÀ4¦izâ‰'âvAå~’p=?À{ìd»çƒiñ~3 >~s^,‡œßøüHag%‡ÂÎ…N˜§¬Ì§åhù–}*žÃ9?Y~ÀoúÓ}õùÞïò·†ÍCh#úÉ4è.ß´ù¬…Ž«áç*váG·ï:}¨ËNØÁ±„As ­ø <ö¦’뮣:¨z&€³¿kзYÙþ$~=BÕú4Ú™™¦ŠÁ.R;“«33gkW£ß¾Rú¿ùXдvÍ8Þ¼ÜXí‡þ“„÷ÔX<Üv}H¨=$<ÉÀ*­ò–È3ϪGËÂ>ì7‰œ¢ä8.X^þ6L‰(Ùÿ–W™A·X5¼IGIŒ P£r]Va¡¾-Qå¬Ýx|Vèr+ïöYì2´Ê¾«¼À^L‚Ër’8ÇÙ³õ&‘†b½»à)7° ÷ª¶”h²Wœ`m—éŸÑy iÔhžÐt-ÙÜ2£C,ý++åŠlæý ´DИÆ%ˆKÄ<ƒ¹} ­_H´ ó*‘¾ƒ"8H–)§1ËÀú²^\¦|¢ì©GOa„ÃÚÖζ>Æ‚z[+cçþ#ÌWƒsàYÿ5Æí1˜Î»ëþÞ&K‚Ãÿ&8Ê,B»ã‹æ?Ê2²hA@àlŠj Sl'_ ˜<î›´xÿŸ.:€"±X•Ôs5À@1gîÀÞëz9§éÌqNÑëƒ •æì¿…Ó›#%7' FØ9ì ÚДRÖ•) €Éfwlãð@Ÿ}/nI¿ñ+ÿYúöß? uèÀ× XÚé'• )f°•|%²TrÚ jN›ÿ4´J‘íäYÒÇ »ºU1û—;!§Aåm‡r20B~âe€_NW_³1=÷ü â.mä4åplÂd-T`‘ »TîhóaØ–…Ç"{ª yÑN`üÔ‰tùíŸLŸýü“i [=]P·Æez CÝ lràoÑ´^‚(˨ìÔm9³/ÃËùÓ>s ªåî CÓðò·ŽÁdÐÓB1ãպꨌ7 ݪ*õ8%¾âk„éÌnKÕ´”Nß5c>ÛW»3Ï9ÉÁQGzÒ†–yikï-ä…n`²¸2è rÀÌcv¥½ÄÁµ-948ð øœ* fà*l‹_Êòq ¸.@Ñ]&èZMkÀÆ¿ÃdŒ÷b Žê·,ëƒQlncêÿc ãOIùs3:º@K@"Êá¦èǬ<À#òmþuã8²‹232 ËHà9Žxå^2@9´Âãøãjæ‚õ.ðŠ®Æ3§,wý9®Ø ïÄ´ì¬ËO¹±bÚxtk Î’¡ Ö8Ñ4üZ§ÅøÜ ¶À™ŸBÁº4 çV±…èǨ‡Áï’ ð}íåëgñ1I¬Ý䂨gÿj.K<ˆÅA´eKå“ÊBvANÀ1Þ]†²¼$pün¾$f·cšOó± p¤@?AyuðÍ,Uã¶gʵ™°à Ô!ŒÛ¹vŒKFFúÙ5–Ë™ g¼8ño#ÀŽ·˜Ý“½;ÍFÙ5Pgy`ÄåbâúUFìÊqaºâã*@aúÇ÷éÝ’¼ØÔÈøØèÑf=†âàŸPÝœ}_÷ÖeÇÄ£ºæ.äˆÆãýJú :øO}ú§‚X˜Nd˜Ó¨¤«ª“†_Mƒ»zà‚øž7`ž|–ø0O?þñÓ-W-IûÜ/¦k¸ŠxýºuqñK/½G¶qfâvj*´ufZ3hòbBeûpoÝNSÏí/:v>~гü'm„§U™c {ÓÉs¤³’FVUNsØåtÃ}Õï=»×ŸfÕø±´s€Õà-,1Í".ÇûiÊôiLAN–ªkÙ¶UVÛÞ*´ƒ­Û¬œyÛ^!Ýhª4³¤dA;–9ãØ–a×…+öÆÐ M€‡íŸ%–;œ¼L ‚Ýl .Ÿ¼Pvµ÷ùŒü%aP®ö’þݤû ¼XÒP öQù.„×yð£`ËÇ|Û \ë— PÚ°%œQí%$rùI@ xÊ`—ú ‚½·ÿáSµ¬¯á]BD?†c¦$>»x߇y™­Ag¼±[€–H0íÖ÷Lëִ߈›Ë0/@L3ÕØþIr‰yEø/ûÆïlu&¿ nÿ¦÷攳¼˜®¿EKÔ‹ìú‚Щcçª/‘Fºm¶ËÉü{ÒLÛžÄS šq ©c?„Wã¾ZË!&‚Íiî–´ŽØªuËá ÓFXJ€“Üßk¸p(Þ:ÛûÆ9À F™C(UgÂ6Æ$r‚Så ÈdÐ ¡òT £+2l ÍT:ŽSÆ Ü q—ÀàÐÈ!/U”âE“œ"!€¸R ¥¢xùo¯wQƒÌôYŸ3<ľˆdvuqNó#à§Òïàl¢Î›93¸z€À¯ÿú¯À9‹u _ð’xæåWÓÏ·,M«¹©ï¶éÙѸF·ª\Bpß'ÁÒY|•°òc+Þ¾ƒéŽ{¯Oø§O™%{üð 줅¬ÏÛΪ1 /à›&ÁY¡ÅªÚ²mo¼Þw×Ó¿€ó`ú%`äBTgã6ò+×IM#:‰ @´úx$NR™s`BæÏô+#ðƒü €W_ÞŒlÀÚØBè1Ãúyä‘GÒ3Ï<å  ÎaTÓPM÷lÏ9ÖUîL¹ód®@îP¹ÌýîsÕ>û©Æ‘Ý5ì,•q•å`ÊÏš¦¡ˆ§|¦…åïúÓ}(ã/ýj“Ón ÷…ˢܳ}FN{vòNÌîΣië©æ4§wiZ²l'„cÚèYzÁ÷ °ª Xô‘…˜Î¾Åèùè|Ö¿32KàÐJ@Q€A6ž}ílS.)\¹-°½=U3ID­€yY¤kjŸ›ò™ôÇ©ãÓ_‰, ?EŠ—á‚q=WÍt÷g¿¯%íkÏ¶Ž¼>H2àûѲÑM DÀ!¨ ÎFØÍ·ÌÑ^ ²lFЂÊfÌSh‰'‡Zgÿ{©‹à”çÕ*{±ŠoÖ…aX¾–§uã2ƒv–‡D‚õcœ«1mqÔi̤J5H:ŠÝ¦‘Y03îZzS™`Δo|–4;àÒðc8Sçß»‰ÃÌ­›ÇRJÉ®/¤šÃd•>̦˜?¶!û”mG"ȼÙ.a׉)§cÚ¶g ´Ž¶L™pEYš¸M6ídM=c!Ü7gW¯ÏãÄÓ®iá%Ë¢«¶wR’-p†[¹¥½5Í_\È|õ#ã5‚lV/ÎNÏ‘ádÙ–SǸr~Ì#~™xzŠëK­ãlÕfé™[^Ü6ÆY-ÈX#g—ЖŏÁßù#8EÑú'8ÇmHêå¢Q%Kÿ †Ö¢3Ûöl&ýÅUŽcPzcCÅv1…›! ˜B+Ç#{Àa*¯ Û:r„c~Ó¥Ä7oÞ׿ `·Þzk€žà#hyàC:óFúµŸÿ4kø40:û›ÇíÄ“J€ì8Ô '[¾º kö•½GÓ­Ý×B L-j¯3öP£Óä ª†ç ;©œåËAp©B"EB  ƒqYÏ›éêMWÇÒENÓô4(ÉvÉ;[ÏØ‘&• %1䬿<Û.dñùîÒÆÓ/nÇÃXzü‘‡¡|çÇñŸùÌgÒ'>ñ‰ô‘|$ýèG?Š;^|ñÅðS%$&.”ècíŸxs§Š4ñ^ÕXIcµ,ÌÑY€‹UÎW aÚ^©òB~7¼DÛ³ãê¼ñ‘'-Êp‹ÐóÇI3‡ñ”nýz–½ù¬|Ÿ áí=½48'-BBy)óaúA+ìËu‹zÒ+½§¢q:\dÎ%Ž×b:À 4—9ð¢e·Ú÷ mûÚ«–įó…t¶;^áYàZßÞÎÏÞ® ~ˆüeù‚¹€fR$`’ƒ{iÙt¡a°tbbå[V‚ÓŸ¡ýyqŠA>,À¾<^Ž…Rþ7`.÷ë JVþ3ØSæÌ0%Ž  ¨×°¼” "±#Oî,PØÎ=G!@8ÄÍ€ŒAû jÛZé~ö_Á[пÓÉ€„€„ˆu(â6ëäÏÖ£ËQô¥øù æ5èÙ•Gù²³:Nó£ªkDMBÑ3·öWÒgƒVl³›ÀôñKqiÖÏRŽ;rm¯Ä\Bú`ù ;Áý•˜PA)¼·ŽgãôYsvçwÛÃÞýVê¨~„9¸ÄpýyÌöíë]ó‹¥ÙV¿^§ÁhæÙ W¹„‡D*øÇÁmŒ«n6[¶zU:~ø ͘'À+ÿ; ˜<2dO°µv”Ó3[¸ÝÑKÛr´<]btæ"!(Çú¶ÝÛCd?ÊþgŒb"jå\4j**]$É‚ýâb[¡Ê%Ádzÿ½7~„%gûM5Še ¸y½ckÝ ¤°f㩀ʸ“Sræþ¿ó;¿å¸í͆µ¶°?öàé—~öþ´{Ÿ­ùT!©_ Á%YólLÁ>Ð)Ýù¬¬ÁžÃ§Ó­P¼HÝK08ÓUyøß¢5KÓö=HgÎléCà|å•Wb[žñeŽC3}éÔ+¥¿×Ëm0MÓ—+<°¢ªVå Eöü¸îïNˆª?Ó-7Á%‚]û-ƒÞ´éÊUéá§_K‡¼óÎ;ƒ€’(ñ8eÏøþ÷¿Û -OýK©-Ÿó–ŸiÓo6M“%S%à"ý¥;ݪ²ûx™öî±Ëáøî±£O6”ßíÓEüöVÊ KÛS({s_ã}š]püX¦/{ ÷üDÙæ»¨V°fÞòþ4Ø3'8Ž|Ì)àŒ#'®5hAÀ¥`"P9øZ¦CpæeÛdt à`}`4Ý9 «<o-æB4#(ÝØÖŒgjŸ‹Ï3üX&Í?AŸ½‡˜>R8h °ÍñÌà‰4\i¡Ž˜}N0ñ­ßEÊv¢7“«×gò3ÝÎ2€âˆÙöØ!  ¸ÀÈ*¾Ñü‰t'ÏËг©ò`Ê©çKñéÚ Zíïþá_¦¿ùÁ“°ÎO¥m?ük³¢œÙ ð™åoc,"”m®ò› 6˜Ú–-»d `ƒH]mÁz)Z«¬á^x!€YBð3àéÆp¿þצ €g¢D°­ªfZmgÈ20àÌ0&›Yú¹AGØäÏr(žMÿÑôé{oHûðséŽ^¶lYúÝßýÝXÿ¿ÿþûƒ‹òÅ/~1Ç<– ä²˜Þ %"o%(æ|Æ’@iW] °—g7Q–>”*ûÏïšÑœ+ü•@®¿j¾Ã}ŸÆ`V¥Ÿ†›°„E‘Y 'ž+áVã ¿þä8ïüá'OzÖ0Í_Ôšµõ¤7_[N¾?@à²O•.wàufè¨ä8È„$'XÉÅ=ó‚‹àá@lŽ˜¹Æ’q,¸»šg(J‰B^Âá4Zprvj.-¤kг(¢šaX¤áç1‰bè–\гø!Æ= B?N0ñ¯$ºÜðÚžMάÊqøu´ñ8ó ½QnÏH²ý¬hË`6eÄ A$–³yËÕ>cY“1lb«d¦¹NYÖÞaw‘HÊ Ne†¸%¶L@oÚe…›ëÊï'Ð.È´ü­‰;Ónßl)€¨3v/¥›x¾}~eo¾‡¤}\ÝBò‹ù›I-‰y¢ßMm[œçU–ûZ´é4í¶‰‰r2ߦâ¬ÆŒ¿ãXê„­ïÞ}Û´'½ZÊí]üòßÑÉ™ô?ÏiëhMó´q\ï3í1&\,…Ž kœea¥ýéýÄ:—ù`¨ÿLpœü ÃÕu‚´øÒ¥iÁâEÈš²óŒIÖÑãLÖuÖþ‡‘Õr²6629eD4ùdì¯wPvÝߦo²ÀY×ÜÝÿ£§^K‡jË~"ýåßü¬zR Î|&4ÞåË—ÇIzº4l Áû©týUkîýî`8×àÕmoÐ(Iï4e½Ìç..2M¦1ϤýþÒ«E¯[¼ä’˜iW99¸q„ZV]º×æTâ@7BÓÃ69,Yb zìö›7FŸ½âŠ+‚Ó wÂ<{ Ðm·Ý–>ùÉO¦›o¾9}ùË_Žkˆ%¾÷½ïC@viÀÓ‡†¸e¾XÎGNgpxè3€‡°žväµ±½ç†²y ¦vµÒ}|(¾7ÜŽ~Ê0 Éw. ò¡ˆÇw_gjJÄJÚ²6i­$'Ò‰'ÃÌÖúUùmŠß2 Å×wþ»aÌ¢8]gG3™7Žƒ¬]œ Ðû*i±ý’öÛ€ æî\íAÏT6gö‚>Å@-à7Vå8æR¦‹%€YðÝÍ©ß5Õùøá5<Ï®¼ˆÆYçè|1ö æ}1FJ„8 œYuU–š×€¯ áøË3»Ÿj+±#qd9þŸ€mº£÷ (ó(ñŸgÞ–³Î8\ÇïÈÊ8K@ ÐíݘÁZ†.»ìÂŽ ¶£P‹Yþñö]ÁwýŠÆ.´Cœ¹<0aÙSñ½àû›bGâKÙØ¿ i‹“àÕ.Áp#ÚüHX\˜²IJ€5¼ì}+ÿ1³j @!À1'î¤Ö‘¶u¥Ë— 7Ï[ÈSÉ1¡Œš!`»¥®yó‘Ö_Áù/RtÍ©£«•›OÝ1å [%³~ÎqÖ?— Õâ:xŽ÷mïtÉ¢œ>ØÚ;Ö ÁßÊ‘¾Ãl—Öt_3˜ûÜfaûŸD?pd/“&†­ƒ»S wô4KMýLDY²Ñ–ŠUƒ˜í z/J®xj}ìÞ;c¦ëº¿[Ù$Tî·÷Ì}¹;vìH7ÝtSþý?ýÏÒ÷ÿ¢%=ø£¡ê¦my–—Ü]ƒH³à²@]:¾=Ý~ã•S·œ8Èî;t4'û«šÆ#!á,]à͇nŒóu–T‹-N‡¾ÑX˰‰/6JÎ7€€p@ó*áéÊ8æØÐY>]¯2Ýùî‚:ËÖ½œ®¾á¾ŒÇùRÊ'ØÉ\pë Â€wÜqG7ÞxcúÊW¾w <úè£Áغu+¬)-âºÍÞ¾áä†ñ5M: €cð‰.Äs(G§R/eà×)„ïÀ|/}†¯IßE ¾‡_Ýžq¢-/9*ËÑe€ðëÈàïÓ•ß#<ü…©üVU¤©jñ.?:ÍLéÌÜ´”5Ÿ­{±®“©Ä6.@ ‚Ä*´ér)°h‚»3Èe˜—aJ, @úœœY:óÌ d8öY¶‡0w å‚œÄt?»ƒüOð¬û T8õB &ªõ«ä!@G:f#èçq_À]©÷ß’¨²yaàŸÓ$ð«³r­ÿY^v~û‹ß,#P`U¹®3zÏ€x(ËàÚqÝv#8¸$°4mƒX‡x4<  ÝZ|¯•n\ôL{T»5oô0æ¥qå.çË¥h‰5˜Ö—Ê2rý&¦é\ʳB€¶Óè W5‚tÿ¿g+ÈÚïú¬~ÍÀÍ‘kn–~TÐ-çWœ\šAï§8Ch¯­pO—²©k!ãŽÄåcãý?'÷õ¤ÞÓ=ÌÜÄŽ¦Äå=M`Åœ…l§ì°VrŒ-ƒ^à£d½Éú±œ]ûxÀ>ð`΂… ŒKÜ àý£ŒûM`Ë¢…œ‹,H_[_š»x(zóP:ÝÃ24Ý€9^Ôä”|;+ÈÝ?Ž-ìnÃÔöE£D¡‹NõŸ9vrñòËíŽ>&¥IÿèìXˆ\{ŒÛøÐéã$À:¬ñ‰!–8²0£̲ld¹ahÛŽýéÈ¡?‰µë/|á Iö´ %ˆ¹¦í…8૯¾ìíq@èEFÑ›>þÓƒœí+±,)g«—ìmgЪ|v¿ mãÒv‚éãOnMw~ü–HW8ç`1Š€!”e¶œf Φ+§±Jèt×~f ¨µ«.I{v+ù Ù>M¹r"âB¨%ˆŽ©Œ£Ž¿,¼çW99,¿÷±ÓÕûxjãà"ó-¡$ÁcÞäšxb¡ö=ôPÊx¿À>ðôs?÷sA< áõŸþâ4xbºdqgZ Ï@衳 й-3;¥J®”{ôÛ”útq—ýE¿ËþJûF_Äov€lœÆSÆ•ãpЈ ¦&¡‘Ã3œj:ábßPÓò~´CEü9 oÿaÕÆB’ùàòÑM/ldœ´è§cÏ3h¦,1³”;¶’|vº£÷.´&Oøµ}Ø;3lÜþ& ­³QÁf%Ú6((^8P#ª&°Í‰Õ àSû<Þ#Í™”1ßFž#GÛðkÒß¶Œ‚{a^\¿÷}/Z ^€Ý1— ŽÄÁ2ñcùã]3DA?ñIÂë§™±6mBà2mMéÈ“éØ›'àd‚-yP™-ËÙ>:}Y–ÞWyq©‹’ ˆ†a—œâ8_ÅdQ6 _üå4ÑIZà¸-ÃSš¸8c„-‚Ó¨4}\\‡’;CåÆþÏ£ôÝï~7­_¿>Ýu×]iݺuI6õîÝ»‘h’ 0uâ2ðTû‚…?µÒ+‰÷À«9 †év=Qu]^õÂ6ç>µ"Ía ¼ %·ÂYÙ›Ó*¾¿Æ!PHdÈÓ €ÁAó“éŽë×§G}‚k‹çMrCq6»z³º~OS¦Ó%÷"3Æ›ãfïêó[]צ•ËЛ/ •eå2ŠÄŽÜËÆ£…-Û»ï¾;)#ðnH¿ãŸúd:¶å¡ô÷?šüñ+iÅ¥õ´˜S Q–sªIyNï_Ñ "Æ¢3ù½ ¢U÷Ù>Û@¸Ù~ŠY†™Á\sÒŸqœ¨+Úë G7>—Ê´ù¦®¦3Ü—n¦•8¦Ø¿ƒ—­i!éRÆ÷7Oºj:5(@{`L^W€‚chúR B…0ŸàM; PAŽ&f‘JJB`üÕÇÑ‚•{ÐWc B*9 ·oá×ýûcâÕG§Oç¹õdùžÔäÌUq`¬û‹‰oKÙO‡AQP×Îr`Á97ï[ÐE›—#RHÚïÅÎ%ÆõàÛÎd`„)ôÈzN•[Ã7ƣ༶qXvš‚¿; äŒ2Çà­XJÐn)z-ïݘ[¨„‰™¬BÞà8ä¤M&ÝÅ~ÿKxfüp Y·Ù]Ï»„ÀùUM¡e]Ñžg'n'LZÄBî˜=\$±XŸGú¿>œ¯%×]œRÉΆ &jÞÒ7„ä8î´u6³´[ÜÝÍÅ‚ ’ý®Ï[ƒ½ìlaüôˆw×î;¸êÚñ¿iêQƨ`ï3ó÷{‡ÅßÂáimôÃvñÁ~vhÕÛÁÇSŠÊm~¶i–çZØ&ŽÜ8í¡AHöùŸN='¹Z‰Î™Ó}È dªÈ§y'¨(ƒÂ¦üuößP“Ï`šxQ©‹•€]rœëQZe¡Áò_´xUê=ÃZ @_£òëlâ†Åf(A¥ýǪ©wÎç°îsná$,ÀM§`£»–îmw½’ëy»mÛ¶ôC$ØÝÒfcpM[PS:-ÐÏaQþ8ø œr²r&,ˆJÈÈ€aÖZ “¡ð¯¼lU:pX*nÕQ Á¥°uÌ 2 ®U¸ì´¸ðèDºõÎÒÐïýû810³î³×ð³FÆÕƒg-eè&–MFgó“Ë)í>à€UOÝ+—G°º›®$z2G /…|ë[ߊ#†ï¿ÿé7󷊯¾&ÝûÅ_c¢óXúÿ㥿yè ·:Ò9 Ô²˜!üª]ž©ë<§¹ÚCŽ€r·ž"Ì2¼x/Ÿîs|Ø ìªøÕÀ!TÒÓS‡Úã§Z{ñ\†£“ª:×·ª»·ó<1Ô“æ­ä Ž·föÙ;,Í?Þ—N׆:Ûú<(ß+eÉ=âÅ@X€Ë“|“@䜭 4¶oýd{AŸ3@L¡* ‹;x×ýÛS&¯åÈ ‚i½"g½ÜîDÊlazÌ GC‡¸lв¬½DÊÖ“j€H=.^ŸW ’‡Ñ ÕíÅ”½.ðJ¸ä!à(²ù.1dyIô[“${ †X6™X‚Vr?”pfo»>‚¹“‡?‡]g閵ĆubZ ×ðµó›ìr1Cy…å˜Wó.¥´=Â*â&Íc÷aGüQg.ÛBSßI… a_˜R€2’AU[Û'$¦ý,NCÏ@¼ŽµXjt¥²µØJ,‰®5ä½þZjŸ×Ãi|b?`O dàš{s 7¸"Ø=ÁdK)ýAXúímCì¼òøt.ÝAxrqúZì÷çö×!äjö¬ÀNH8ˆî€¢p?ÌÈu· "`°ÏqÚ‰Úüu–‡½Í_q_FÍmÜ£ý±ØIpúÔiÀþL:yƒáõ©ôÔSO…$žÙZ(ýîã˜EéNáÁ¬²a!C0”yä‘à(LׂV}á“JüxsÌ’Ûhì®GO@ušjû ×8;£–Ý.èUÓ¦÷ÀC©cå‡È%Hò̽ ‚bOgèT¬í8(M‰¿Jh„ ð¿y×§ÒëV¥!ò¢¼„B&”ªAJ¸d!GDBÀ9ÍwøFUw©ã#é7ÿôðÓoýÆ¿Nß~äYXrE|ÆëðÓ ¦höÇ©³ùFw³õS¦ÛøÅÊ·L,LIÇ·ºaø2œREØÕwÃÏ.‰¶ô“eîªaå€ßesþœNfKC©{IKÚ{´%92žN!ùß®ÐR™[Üš÷ЕöÒ¸¹øð®µhA]p›œ³~ÁOP‚ˆØ ’ê2žuÿΕlÿÑg®+´v××\Ý>HÀÙljSH¸Ðs†_À?˜7Š9NÖÆÅÈ Vöá«É×J/’ïÝž¥”¨ßƒÞPÔzÁ­8˽©¥›²>M)}÷ØX’m$¥“0Z»àvp¶ðœ‘qØ4m]§XÇgìh‡»¿O>| * ÜŒ°Ýh¸Ö1—½*§Ç ¼®Ý¥AÙïpJ8Õ¨gËýNQõÃÎïB8Ð5ü‘a®=oOmÍs!*‹ OÀ¸¢=Œûs!Xôa&tmØ+käøé@o>Á¤eÂsì)egñ‰=a—ßý^ôŸtR¨ò­Ä*œ ôSº¯FÇGlT•ºX ‹Ó9,|ʫ䌺-ít*¤¦Ôæp?ïtplcs “@‡r¬q9{7=½©Îì»꯸ä…Óðʵk‰g¿þç‚rº»»c @®€ßÏ¥º|€ŽF@€¹êª«â´< Šl'•é€ñÑob—B[ø`gAÒ}ÌD¢±œ›À,`¸‚­ Ùôºvíüѧ·q…ñ‡ g,¸º­*—Evçà ZYŽÕïæC–¾ëøúU¯y’x±NB§¾—ÒOܳ)ýÍSŽ~›‰Èa[Î ù­†kð…/Ý]ZoMo>ûlZzýUŒLûTLý~û÷ѧIÿà×~?mÙƒä:ÖQ$Ös™Ÿè˜¼Gs໑DÊd‡ÅrZÞ±i¨éáYèÚUý71(®J¶¿qoa¥ãâ[ùªaiWè>Wœ5‰0 Ç´–áUݽÝç’ꘛö±#¦{U_úá@GIîAxjª§íXº+0e×_fAP÷Á¢vV+ðÛþÒP:r øZo e/ >Ÿî 3oE³-‰í§T&º—¦?Á÷”’ŸÚÉ€c-ÇÁËÑ}ø#úÑWñE6ßšØØïcnŬ+‰ ãuÛœa<ËûÌuhã™YÕ¨Þækðu_Q+Ùœ ™Ô×Òà#Ê|Äë%wǵ’|…Ù´'z‘N[‡¬÷:»¶è_´±ÀzÉ¥íéðþV>2]#¬µ{Óøè¢ä¹e‚Gº¶3Û¯!Ë¥pÞ8„Ã0ùã›Döœ¹ó¾¥‰Ùµò^,qêN+µ²Ep éýAÆ¡:cí`cBËïø±‘t®¦fé¾èe´_fM°h‡1Zð˜¿jësñÕçicÅ”0â…Uê~©´‹J]´K’Ú…’ª¢¨[Ú¸£Ùu¯qdháhÇAΰgàE0£=ï®Y œÄµWþv¥^„BZšê| ›JPœQ»f¯TûÎ;Ó½÷Þ›~æg~&Þð#Gè̳(®:k(ÜEoÒs¯¼'éiwòä©ôÊ3ϥ׬Ou@Ú˜+~íˆgfV†%‘¢€ži¤«€\-Û÷§ûñî¶Ay:À’»~Ã*âa¤Œjj\‚´—!]v™ ó{Ól¯ùRþbÛ³ÛÓ?ýÁôÀ„<‚nΧ:XYÄ¿ñ;ÿ8œÞ{ûW &¤îÏÜGúç¿õ‹L¬î,‚™wú½|4}â¿þߢSEèÄ! F\š. šjTl}/ÓS´”"ÈêoNoÃOå£~¦,+DgÎ]›Óóšã›É4\Ý—iôUé®jñá]ú]Ì,hÕ‚CéØž‰ôêþÖ´„݇ӽŠءiœ‘¹-ñ¯ÅB@qöhª !2Îǘy L'ÐÎL}Ö § ›:ŽQñé^€“ ”•íq´=Zü*LýNU5p©ã—˜9~" »øÖÑXŸêvòÙ$gÊ7-þ$à råäâåIö¤0žž Xí®¡_Á3eÜ”ö3z‰A÷.ôrìA¹ã71÷ð~{‰‰MÔ½e+`Ëa¹“¶‹›¢L-Wߣ2ekËPXŽ)uÔ…c•qïÆÎòôà0/G[ŽG±w)àdž<âè“´šÍØ>Rö ÚJ4òá –Í?`.¾$]OR iêDË2{ê? @sQ Bªédê˜{*ÕçØÈ+5±45·m!cIOB:-kI#AŽ#ñ„\ÛVˆã…SwŽïce;òÎìí?LšC½«ÅkyG¹ ¶‰}'—ýØ·»æÍá0Ÿã©>ØÖ‰`ÛöNž@ƈq¶ç4Ë Eߤœ,*Uô×Òô=:éÔžšú9+]h¯9éš§É—pê+ 7¿þÿ@”ÈüpÀÃÞ¨¨ÌRÁÏ ‡<´°•£ŽàÈÇfº­­£ƒ[©ÔXêCl/³‘xpHj²ï“ðëH5— üoÄîìW€S ´ßøÆ7ÒÆCjÝmw²ñÝ2è!?Ó•vÀ#à –‚u&$¼V÷ᇎFéá@;‘ Ýxˇ9‹šeŒÃÇb½‹&,©éaçw´áÊÚ÷ËE»òGLAmÿ!Z:ëz"j: ‘кdaâÒ¡QÒ9“z™[þ<ÞWùã2^ur'Œm¯ïKWÞõ™°—H˜ÏLáêæîÛ®åÓiÇcÿ.a}zÎNûï<Åî‚çÓç>z[ú'ÿó¯ð[ÒüµË"O.[4˜GÓb'ÍÜŸóz½ñ†›œ¾á!¿5Ì£ ',+Ϻ¶#g"@Á#wi4„ÿr(•p£ãç´•ß±VÜù©aŸÃ Ëm«.Îýœ‘)]õqÄøi¶*]¿éPÚûbWœ°ÿ3rÏ~P0m´—”[4q€4]É»`¢½*òÎ èÛÐø’adoptÏ4‘ï K N¸½‚Ä„í° YØKyžY5‘„è¿Ö[“Sªá:ØÖa³Ò²,ÝÄuÿ–’Ü&ñ­5²Æ ‡çxXŸåû1´uáö>ËC€„çCØI,}¨´ØU€H¸‘8ÒïµünD æà`.@?‹–È—`Ò- ^‡€Kà‰tiÄõýµØ¶D…»-@Ç(_ý¾C%QÅYÿs7a09©Ó÷‘yJã–¹ácDZÏàk·M¡DÌÇùÕÄAܽq†<ÉD߈–ˆ°L¾Œ¾5ýö7‰¾‡V]¸Ø(x@ðÜ‚Tçê¥ýiÏ&0ŒÍ­Ì´9¹uÁÂæt¢‡eXìƹ ¥…%¶Ô‡ W½ ®Ý~XåK2f³ÍnÁî®y é—=¬ž&— ÆoEŠ¿™I ¨¹<ÎØØÏ8ÙÅ‚ÿð[ž‡§]ÛO²ÙÏxSŽ{¿ן0}Ϊñ±øækÃ/Õq¥2þ„³Ä,¦Q1–Øì\•²§_”jd¨ÿpšgÑMVžàÞ +).¢CŒS±Í4D på™Ï®·ÕRû8‡¯avx#ÞLÉ âzuæx¦½ð¸$àév z+žçˆ”¥% X*t'Hë_Vºkã n-”€ðÛ¡C‡8¤ÈŽº/}øÖëÒ_ûÑtÏí×ù±'Ì¢ŒO"E çWW•;úØF§ºá*ظÏÂ{Ù‚4Fz¢CIÛéJv¿Ûî«_ëmӬ΀ûhŸå6ßb ‘ÈY§!Ϩ§‡W}÷ò;?t}XýËßÿBÞa˜Îz†rš?ÓÜ™•þÉßþ0ýõƒO¥ŸùÔ”'B›äÁ4d0®–y#lÜDwÅ´cÆs Æ ÷å·ðÃstB;né.üiŸýåÀÃŽ—"Пa©pïaSµ×K—á.ÿ4J=Ç?¼Ó¿¢Ç.IÇÎŒ¦cýõ´ÿà’Ô:Î,pœÑ¼À‰þá¶2n®àÌ)‘ZÁ†‘;ú™³|ÞË¡rë¡_¿ a'7bsq­p=ì9VS¿šêÙÊblô?BòÀK–yÓÀ·™ÿ$ö '¯ÃÓL%™Ãj~[ü“¤¡‰ ú£*ˆ–]žmš®—Ió01ÜÄóú³˜Ê13ÏJêïÄd¹)fâ–‰,vó&è:>Yfn!{óALïJxÒ­ÀNùÖz·ìZJë±×ŸÜ—70-K‰ªëÑ7òlýîAÿˆïÏaZ¾rI‹Û ‡ wDªGî€é´Ä¸/$dßå‡ÔYô?JeþM÷Ùª6š…þ¹ýª”þ‡û×™‚+"g‘¬Lo&Õ$C&¦1Í¡ª$NñÞÑ>˜–µœJ§çb‘„ƒÐ êäbÞXœ’WŸgqÁ>îsiç’!äTšYªêœËA?´ƒ:÷0Œ°£ÁÜ<Æ2‡&¡=VXz䍿:°Ë cccLØÿ¿ïÐV8»Hì3ÞÇu½eÅA©lS4>þCåþë‹n«M.ûÍn«nxÈvz‹€J¯.±–ÜêºÐ_N …ê J°ôqqöî‹RA¼‰ àÿËÞ{@WvœwžßCzÈhtݺÉn’Í$FI ¢(Š¢D‰´•W’Ç’­õÎÙ >k{gÆ£õx½Ça³|r;[´ÅÎô©oZ,ÎwRŽœ¡žÝ»wû6I)ïÉÒ_ØQA‡àÇϦ Ë»ô-hãbÍmùÿí7Sî vºœý/Ž„ÞÑ·c쯭««á¨ãûÓ¯þ“+sê0¦¼ž|Éç aM:…“.~ùž¦tÂ9~OXÎó)œ(ïeêfE¼·›h3äµü¹…s€Þ9ÔNŸ³©âj–y ûLÜ“H\Ô¤ž ª¹rÔœX€ºÎ"2ª'oÂ)„S¾€‘€T7åŽâ‹ý À*`2Ç8ŠÛ-Ü͵ûjQs¢²x —j&+@[†2_%êÅÆ/蔿/Êx“+¼ P­åÔŸ§¹ ÚYÿuã$ke_¯ùq-ý[éDa?Eä1.µ 4óç–†¾Þ‡@WÛ@;ˆ¹h+àAšÃøºoãÒbD}…çx?j“w;ÍCz½š³Ó\B¼¤X¹_m(^pBH›Úïà7Bï{€| ‹» ”¥/ƒèʯ°¾­¾U _o7¾æ†¶/J̰Ÿðv.}k¹å냾8ŸÃ*Á9ÚødûˆÙÞ·hÿ€ÖŠI.€¾sER;q€}ÿ*šn&ù ×ßà,Õ6W °ÌüÌŽÙl?'ïÕ³g^ŠwœÈg¶K¢›µ(Îl‘”íÐðG³М~¢Ž]U …plÇ8¡¯‹€Å(Êr_nv’Ý]:®wÞŠ1±›C?grz”“üÐóbËöà0\ií³ê‡9|zŸw>‡¹Óëñ‰®@ÒE³ßc˜¿Kwäó2ŠYrËïˆOp«“ó_E‘èCjðl*·ilþÀ žƒ+$Ó]Eòšz¶øM‚0¦ØÞ—..DzÜÀ ¥{Ú¡“– ¨ûDk±NÞ»˜Ó"­-wªÒ ЖÁøÃvìØ1»ï¾ûÜŸîîn &¡äó2,O$" ú¾ð‡ßµ/þõŮ¿ç?Ì'›9o-ë¦ú"Yü$uÖ748Ò}æ#Ô»Ì÷zÉvun±cN¹'+ˆ4Ò±¼Ç–ÆÕ,*¯¨ý³gÏ:¢óÖ·¾Õ•e,IíÊiÂ85@¨Ö쀕Ø3&~´i …:T˜°òà.ÑE>ŸÒ.¼¯ ÚH$½:ÁB,À-”Û” ª ̳ @ßo ×~ÚïãGs bk   ð è»p‹ÊÓ‰Õ+9ò>ü3\è㺈€ #B¬A7|Ï»!ö¿Û­ø ¯îP ÷ÓæBª´Î9óÆ•÷rça;îÚ\µ-ä9ßßÔûÎ>Oë/½—²x¶(*›z¢¦Ìu¯÷¤5Evü| õü’ïïÂ×ó*][ ôŸ#ÌCrŠ\Ô¾{:ˆLÜdô`1ì×g¼xY!B*À„\ $M–êÐ÷QßʸÇνsôMuPP+—¾•Ö•|ò0Á¥ö߀/O/HÍ·ÔˆE¿VÛqu½ø^ßjÃcÞ{3û!n ¸”êÐs]Ëu%ÝîÐóhÓÂ[¨n‚a³X nØPËŽ$̯w–ŸµÓ£K×j=éLZ%Ê|““(1W÷""ÀèF€2eX唽éù^«©‚‹‹Ñvv[ÑlÄ•9ßl†T%¸5g­èF¼;Ö~dÊÝ;æãGs5éâùšŒâñ#|Ý1­=>”÷{ÞTˆW= ¯p¡Š|’sB&¥æSXBršH›ÎmZ`r´o²¦qÇxj±H#œ}„YìœË  Ø<¢ÓÊ+ê­”$%©iD‘yÇùyÌ:V6²Pìd&Îì0€ªa¯Ã‰š0×¢-*X Bî¸ãëèèp#7â(_’â%ÝÙÙ逹çÔ©¼Ž€µŒä¼tLí—bøRüöç_ýc0×™ öHÈC€_ýioo_Öžê•2â“ûOÛ{ÞÿË=öMo;YaZ‡#0ÏhñI¦,……èà!ÙóÒ".‡€s(§ŒnH^zã•;ì™.YÔâ°¶+Icä§ŽÕÊæ»=íïj­Üª{``˜#:›Pæ‰Ó»WwÕŠl²>é4!åâ žœj!Ÿü$ÒàyB9Šzý‰{/§xþã±æ­(ž5‘—Âj})N}%}eQ¦¨ÿ^&Ô¡ò!2½R?5„,´Ð®ïèµ§”Ûp–{(}+x‹BwŠRÀH@GLa­ä¢jdÀs €`(MT¨(`±¿/#¬9´KÈ ¾Ó.ÌKH¿¤`£Žî€/¤ïˆ€¿ŠÏØ»øU½Œ«¹&O™øe\Øs‰º»ZÖUã´vQßáØ`Åyz˜Ì+&Þç¨â•‡ºVo¥ûÄ%Íóª=ååR¼ÚðxÅÅyC»ÉxO }U>Õ—QÚ«åRp¿ÆšóômµËŠúl¼uƺαz˜1á€Þÿï)%`C/‹7È\ݽÌÖŠ?¿ƒ”=¤kiè$,€(9µ€›¨H5…û¹D~_jc.¥)ù&.pDÉž3Qž.U|"Ë­^«Æ2¨ô=tý åÁSfø(2þsáSCm¢¨…°èyL“ Ýß“X袸µ]r/q¨Â4ôî4„I $äˆ[Z)EFú¢¼?EÚI:{”‡0gyX”5u0P*R®wNwJóºõ>Å%hǯçÒ7B’%§´ã\Ú1p_H†^ò9`Ÿ=È{ž¬¬¹±a'ÛîÚm×eU6SÚŽ2<§~-0=o¤ ìçØUjJðžrѳ ‰ØÊ§f-e[ÀéD|v zñ(">Ûî­Y@Ï"kª=gƒèòž@DkØÛߘÎÙ™¹ ËÌ[)ú(S³e¶}+èÔ\5¤»l|® e?”ö0è£å¡”Ýclћˌ°– ºr¶zµªÓ<ŠçÖùéñ;Õã(O>o¯ ú\Tº"âúòyCºÒä<¾ô˜|(Èþƒ¯<NKPlaa¾Û l²ŸÍŒdض7’BÍÓ¿/_òcI(èdú·¼¤Þf2P÷,@Ò aË º9dÝ9Ôd s5ÈŽ8) …‘:Þ:(Òó¡OÚg>óŠRì7@º?ñ?á ‚B¤ù/ äò%lÛ¶Í´€¿(y±û_ÿûß²-îO½+GºN£ðÕê½€ wA€8AñP€DõÎÄÖ ËÜT¦Š%—“ ƒ1‹‘‹ÇT®åÔwYHð’¡]êwp:Áï›_yÄÞý¯Þgs_N i«ùeé»é —{Ò‰“çÀÞ/Œè(£%õ0 ¼½Ç+'9÷l½vÍar…¸0Ù‰ Oë¦{ã¼î‘'Ä9@&rP¯ÑTÈâtùˆ¢.SDI«æWR¨?øqöWÍ+¨Åœ±BäÍ'1šFéx< þ\°J `C! À µ€Òf¡ðwD˜gUwÊRRÔ¾€ž.ün.!˜Òx×·+úå9IèrQ;,hu«ì#€ŸŠêºÖ~…€ú¼–ë¼·âKwqoè† M1ÿááµJ.À_”|Ïy__UmóŽàÖE¢íÏoà^€YT<Ð0ž¿!M¢Àµœêûþ0ÙõÞ˜) T®Þ¥¥‡!ªyÏz÷Þ±Ä(¾…OïZÆ}Úñk¢rÎEÐN‚G¹Wýºô ¥7 >uÖ·Ã> "Š4²öÆt†  ¦Ùºª_FÒÆQ®CC€&Ûî 6£d2Ūxo—\M¤E¸„ž_\I®á*CŒGža†Ò(CçLåéê ÷>â(Û¬‹ÛÆY[ A¦­p[ý5èGa€¬eâz§ÙÜ–QV9ÄVlð @²Õ[9¼ª×ŠØ‹Ÿcÿÿ`ÿiËœE‹-D½ç¹xŽ{¼OC›‹â.P¿0çhÍ:FÇðÕ?µ—ÄG6ÚˆC¶NßKØ XZÄÒ |¨ï/¦ÚžuVÞʼ™ì°Ö²­VÛVj£|öFþºs’¡ÀT¶¶"Ô€i°Ž×Î)€™¡€èVýX;»3Ðâ/³É¡;>JH&ßÂì˜Uq:–Rsä©ÅÛÈ,ƒâiäÊL)[z៌”ZC+ç± r֊عmv…tºÀ&º‚¶ôƒœp|Ä\V qa.¯VfåUž07å‡{ùª‡7îßE߯o¹÷xeˆ£¢`þ>q»TÙ¼[º7àT>i´NoZ‡ûÉèÃÑE¾IYq&ű»•[°ÿ?ceéi¶“`üMÒBñô¡öËË9#šíkF{a¶ ÙZQQ e‹µ(ל¿øc+ŸX©(ÅÐÎ(*iNXls™¾óÎ;í#ùˆŸ„÷Ýï~×:;;Ü_Hƒ()Ù5¡Å?04nÿþWÿˆÇ)d;ÜŽú Š_ýVøgög]K? ¢œµðÜøfì ÔŠzˆœÆzÖ)odœe­ws!§g‘c €-ÀšäÀŽœˆÆ0ì,§Ö/T—ì{_qY8б&)ü\¸mÕ¥)ãX78G(ç@>L\%09óSVaÒB9Å{þx/MCOPieð:¢›8¿nâz‰¡/Ëë yóþŠ:òñ!·¯:ÿ9Üø\%g>ÀBm´‰á´í,^°ã ¾‘V>=ÐYô‹@"“K¹° ¾°S¯xðâSÆYÑøUÔÕ/€'_€Èây4ï4–7¶œèÀ…^J Çà5Í<z§Z‚”¿ ›ƒùÞîýÎO]ºóMt‡¸],'ê'ú÷gŒÓÅÚ8u³-6¢= ½EœÖYcûÀ! Ù­4 —3Ó…6R~†‘€Hs®ÊÎe`ï3,šxĉý,œ´á^Ö†i+G2Â2a6Ï>Í©éã6®LáI;›ŸÇQæ8§Sn—ÿGÿ ?^Û¯²óYaˆ›ÎmlÆþ3wŽÅ^¡ä*ÎbaPA峳߷|,pLdI@…ÀÊòm *öß `ðW€¼½ ÅQè  H—¶Yn6y׋ˆÊýâ¾h?óžOÚζ;'>Nly±ÞE‹UþÕ¯~Õ é\QßJpVy‰D¹ËÚžªÊŒMrd1O1ê‹9å@“¯KÀöíÛ­µµÕ<èˆdçƒ'ž³Æ7¸]ìå»-T ƒÁ¢ìõ[}¯<€©GcZ튲æNprbçE‹rßÃsiþiî%û@ú oæ^ßNHuà '.bÕ ¥A¿Ä%"Œ+Äωcr•Ñ;Ö»QU\z¿B¤Ÿ!?¶Eh v›+ó般°*¸ã‹6z¡„WqáN!ßïáÀž­ýVŠþHIvÊ2ãØ_aÛãô,Dú¬Íô±½€ß;t‚A¶¢ùÀC¾<§âúÑOä…*=ž¤•y”=éòùÈæ¥ÏS‚÷Ô³Æsx© }óÈ…¦6åeÿ!'¹³˜zÈ6—Ël>3Àêi˜í¡×›Íï‰Q^¤¿mô"‰.@æiO9êM÷Ìì2©)Κßáì8ñ쉮Å$%JeâÌÛÞ+¯„EŸ²ïA¥‡Å9<°X÷²í/€œ€`KUÚªôÉ~Â{ñéä}Êr²Œ'BòwÜq‡[ðµ¯}Í5ø›››]W@¢™³ƒý¯ó dïbÎåâ Bç9Ž€D¨N!ÎþðYû€¸ˆâ»k3ŸÏ<€9â‹9!5ª_mJÔ!d#8½¯“gµøÍØ[n¾Æ9êÓjNí—W@ýX‡½t¨ ÀX{wµÌ‰¸Àæ3øj;L:Ïν†CH÷4ÚÌçÓÄŽóø$'ì~Ü–ò ¸/óã6¢–¢ü!=.yª çíǾG¨}â¶£ jˆó*=.둯òO%²×)dÃhQO§«íž¶9¨ÙÙ«!ÒÅj¾šKTþ%\bßk<èà›^|õSK˜.Q¾’= Ћu«t朳¸wâËIW èíNñ²2¨¼×ÅqëðJ¨™¡U´'zWÓ âU¬í&a¾Ï:£]†ƒæÁ™‡ðÀ“PP—Ñ‘‹µ+!E@û|±Ùµ–\KßøuÔ®(|ÅKဠå.%Úè\ ¡³ü… Ëf¿Þ™Þ§ˆí¼"=»¾ê¢$*?°öaW «™ –1&µÑÅãPXÂù â6òýʪlKU™ÕäG1˜5‚%ÇÉ©j´õáhn­µq¨ïÙkÚ^mÕpíʶ·ZgͰ=Üs‰íåóìnƾ>]˜a —ô¯ ÐCwªA–ÎÑÝ‹)XÆ”Æäîl¶Øy;ÃV ‹¿cyœ,âPŸ¬õu£dÍ©?“•ƒ6Õ…IÞ Ù®×Ò>ÌA;YC£?‡àÚúQdù'íø¹>Ž÷~‘ñ®õMc&Ltý†¸ó3™eYØ'$1Ñ´Z½NÏCÅš‡raιOœÚ qù —Ò4›C÷›Ÿ×ñüöj<þÊ£°"ãÔ¸Ö5P\מUâ¦rK+ü¦êVÔ™Üü\/‹ü< §xaþ^µˆº5(h)VîçQ,à1J`7É&À$v ™@“̡ӰžÄÖ[äàˆJ»ñŠ6ûÀýo·=ò0UsÉ ð‰eŸD`~öç>i‹G™0;.c œ¿ˆ;`†M/Ù¼(~à”! mÛ{ê©§|á)¶ p½˜ü|©W,jÔ©gVB*$Nâ!{r>¿ï }ŒûKÛ›ìûÈÍ…$s(F£ë0úfT¹àdîB4ô.VrJ´ÛB[À®½áŠeÈÁÊ u&÷e;4Ø~¨›·/+3®¸—Ì_ÏO]ŸFáù/çi|‡Gqž’x¸Wiy§+o(›Ï§IwäÐ-¶¼Þê×=á| ž/”ùtŸÈ›o+äÛ ¯òá]„¢É¸)¶PÍ—MÙµ˜þþ\Ø‹ö޾Ɗ€¾ÞKøû¢:À¬…ؽ@š(R}·J®(§4åû¿_— ®Ë§•ÆvYÊF'Ó]…ߨ£;Å·0]gqêÙ…sßûÈ}œÀ™"Ú 2WÝ" ÀíÂN@[ËüõøÔz‰ž",ú½øâ~ˆ#ð4áã\@‹€¶ÊÂç}E†|ô~5*„‹S²Ÿ÷ZÑ–RT⽺§L¾?JŽ/O e=[Ô²×©Ä º•À_Å“q‹Œï­èiôV£ñ=ÚcÕÚS]Ž&6lĦÀÒÖ2i¶‹êÔ6²9|,Éoäz3a „; ¢¶HR‰ Z¸øõMÅB׻зTœ8?ëw² (X>ÿq§]»¦m¢^» N4—Ù@_ Nãìh ûÞüR8Ûi¾ü,5œ0û0Ÿì±.”·ñ„¼¯h£i>×Á£(Žò¹:Xût:i+gÛ_Ã\XþEçà6·Ñã[¡¥ÊlkùŒ9Öo3ÛŸ³£'ŽXÿÙç¬áÿ1½¶×Òé“$'n!NóÌçy܉?äStˆ‹³ä½do$ŽÀ[Ydå}¾±úÅP†NÝBPƒDmÓ¹Md¦ÇeË’áŠQhÉLtfMA‘uÀÎö© ¶ø¥”S°¦ÈÆ9ÙÌ$ ¹T3ô6@—½L¬ëßôVfE‹½ýž{ìoþꯖ} Ø›oz£SØ’áË  ÿÁç?c×¢»×ÕȾØ0^Ë)¿Xòâ Èÿö·¿7 ¤S{ì1{æ™g\O`½€@YH€ÄÃØŠ…â&ø«ù²A`Ã=öŽ;®)‚Rgm—"ÞU{;éîIô¢gZ«ï!^Ï'C"µ—ttþòï¶ý©Ÿt3¿É´dXb–k/o÷¨¾~l Î¬Ç °j.i& °réù’¾2ø=õ1·”3Z’yqÉÕça¿U©%ꈾI”æaÍ^• ŽpâÎcC¢²QFK´Ò’;Tùjù÷ ep‰Ú8Ü ^llFcµŒ ªÓ¥}2)«)/‚Di@¿ƒM‚âµV)8gúo'¬:‚ýSÄšÝ\[¸¤Üv><ç:ØÿÚ[^ràµ5ú–MÚ7ï€w­Êö8ê‚~©+×ëQÖüU§8Yz¦ð\ Ý\=\zv²‹A„mçÚAo.ö"'ä ŒÃ¾/ö¾ÊYµF©3²ÓŸÙåïŠ?öÁo9låÈçuhJ|‹œÙPT“-™%u¬_“¶µDŸ}ó“i þÀÁ,idŸÁĮ̂ ±–µ·•ÛŽA¬f¦­½£ÄΤ[Fª¬´¡Ô²Øv82Ðf×^ªF7^¤ë{A0Þ׉y!pv$SÝkß9Ë“Zow‹íÚòœí?ù+ŸŸ´C°óßP>gÕÍ3]œ)‘Æ [ø2ç^²Š^°'~¸µè¤åžëå9q<úr!Š^÷¯^W˜@Iµ ”WNùtÉ…9–œ§ ‡t¾€Ö‘(oä-µ—ˆÏçWq>+É e ]¹–ü|Á¥¨8Äüï;/r“Dhänf7ƒ)ßä\!,ýY4RË*PD–&EÀ g:ë@ >ÇžøÒÒ:äâ(¥0б‰^ -s³Ú"(%ŸöŽŽ

ú{<¯ü"ìx,ÞY¦‘0;öéêKWô±N¡Ì饲h^ÀÁep!³S ò‹ÆwlÎàüXðVçýz²‡ŸGæ^o¡ÛÖ6kã\ƼB8i…mâ^¢Ÿ³±s²ÁjÛ{ÑÔ¯´t}›mßÒ0YeÃúÌt«£~Ìþ¾«ÆÞÅç,—×Y`'Jä8^wbªÅv6Á8|‰UUœæxÞF»¦¾ß†&²vôXÚzGºì‘'^´ùÙ­¯ñaê5€az}r+ý(6š³š—!=¯@4÷C”/š K‚r óVu(-dð¹"ãBÞ|¾áSUØ»âÉ~ÿïq+2À‘>“̾™ÂKu3õj©/ðõÁžSíá­Î#û¯(­·0gŒIZIºà†n ›"óO—Ys`ÐÄìòr0i&œÎ‚®­­¶¯ÿýßÙ[vÞgÝ|ÁN.Öo½óNûÄ ?a¾ë½ÖþÈ\^r÷·Üñvz°+ÃNö®Ýö£¡Ç—z·FH€]QåÞ¢àøÃÚáÇíNÚùèG?êJO>ù¤Ÿú±ZuJgAõI;_¶T§îuɉ Ê}T\Jñ–-u(ý°ØXÅŽ­vèñ}Pÿu8µ'Qƒ8rBd‚ØB ãto„ó×TV±g[öSÈ{ÛÞ&ÊpØæxvq1Öã”OzJ×€~$ç‘úUÀ„VœÂŽ˜NNÎ|šøÁÅõ¬È!Y‹„×çq›§˜;o'úê–2‘3ß×D¼ÇÅ÷®tHØãå¢V^þïÒ“–Û6 °œ`§ ¥ªjÞ¦JÅ'1ƒ¡\ˆÊÜDõ 9àÒ¢®i‹›(ÝN.!úÆòGy¿Bxam;ÈÕjà‚l÷ Àå”2ÐÅåôÍ׿¤,¦W\…·”NÑÕ«£Þ½u*[ÃÝ`ÚU¾‡Ö.‡ÙõÙxÄÁæu¹É®5¹‡KÏ/å?!z¶•NÊ€BôìzzÖ~B'h¤!…žÆÊŠÐ?*¯cŒ«„30ª°?»¾6}IeC:)«o–Ȁê(C“ÁZš ÈرTÄ!fc UÖQÓf…¹›€‚¯ªAñn¬‚ùÞJ~ø4U}*ÝŽi¢ÎB;vªÕÒåiÛÎk¯«’>ÛsõˆÝ<¢P‚5N¦^/;UØ0¸[-­÷Òn³9^ÈÉ‘UÖvzSÏÙ‹‡ž·îS‡ìÁ'°ž$uùÕp÷šëqú,kåõ*V©GQ>·VI m«^wäñlq^'ì>?j›2JUTÒʯø9ÔQù¸<ê@À`Ùœný³õ_¦ÿ…ÊöŠOHô]ðr¼Ô9Ì~•³ÅoŽC¨S$µ3?½fÝÈ.Ød•ÛYF>褴cqAuýV¸s¶­¡ÜµÝó*áq8@IDAT8õnmÞb3k¸m¯ÕÕÔ9°Õ¶´s§YipÙŽY-â†õ8Õ+ûýúø¢ÖÅ„þîïþή½öZ»õÖ[ Ã…tòžò­åð¥¤(@"‰(„ˆ cŠƒ¶>º»w¿íä{ÝÈù¼%vÖOä¬Ã…>@;ÄpVÚé>-€°·.?™pyÕèR_w…eº÷cép}œ•ÏO4Ú‘óO‡u¯>ÈÜoˆw ®ôx²&}õ5 *«Éœ÷Cþ(&_ŸnE««¬Sú´v&ÄY—å ”çõ‡Œ^W|C{jÝó%Ò_­ ?YjÐúðNhÇv/ Sܱލ,Ë3DË>}—@]5ÄÃ/vŠ·_À\HJÑ×þ@W”Aéó\úþj1¯)¶Feˆú8r·Wx#hx‡¦UÑná ¸û ¿þT~þÏA+"¯ÊN}õà%>×ñós# }šÏBô$²xçùY\ð9â–è@Dâ=;, ž=bùÖGK!]]ÀN#Œ‘•ƒ°)-ežò¦ü€2ÞúGóivΨO!+™©cKÌå‚d³Mò½¶Ô±_¿¯A¥í,›µ,9¦Ñ¨¬€€h¶ Ûl Ù{EE¹=ù‚ÙÍW Zs'€æÍ,ç(ߨY`O­ˆµSO\êæŠÆ+mðt±V‚–ÝÖc“Ï·Zß\¡µÔrÂvÖ‹éV;=yÖ¦ûíä‰oÙ÷¾sÈ^Ñü¤ä»z\˜Ð>Vô¹£ø0 UŸ‡ãt¯?Γl+ä g¬\ˆ’õ…Qé¾êM,É:CŸ½®5ò$óo4ìíc!³²zÌ:ç°ÀVÀi/RÐ¥^Àg>X¤Ô¶XÕ5\¢®# äV¼t>($@\ } }s!ø9.! zÒןæõ|ׯÜxªŽ20$ÿŸýG¨z¸"¼ÝÕê\¢N¥X¸w½@}mKT(¤X[!áë¹%`]!&² ç”ƒ…b qbY=”>&n ûölåë.‚‘½­ÒjÖ¡iŽÊæ[È€™,#qZ]Öšœ›ÌYk# ÃÛ­ž öÜÏ’¿S03X6íƒ(ã ‡iÆiƲiEuõse¿ÞùYÛ]Wiõè3} ‰¨“nÉaª·Â.½’³&*¬vל½·sÀN÷cpü iëf½›z-Í–¼3ý ŽÙ×ÿñ+öâÌw8RwĦ„×¼Nã:9·6R§^wÒ­¬'Ôís‡Ì!¿OFý„ã°rˆsÓó+>$$UTHQ½ï ›Ï¦¢NõÇu€âCyŠi+äfµ¨ž._i³ÉÆ€N.{éô.1@l·\D¹^ÀäĈ#ÝÛ ŒgdЛ­ QA×)¶¯°—¶"]c×î½:³×ÿç?ýÖ÷Ù8Jkmõ[ìXÏ ,}ðÍ»{lÅ[n¿Å>ÿÍ/FÇ[^ýG[9éD¹ H ˆ øÊW¾bW\q…Ý~ûí¶sçN?epß¾}Ž,$˪Œ—@ºâÈìp]]?»ê’8@;lØÞtç=ÖÚòˆ@[ Õýìþã°ñ׈C?U{j?Ü˪ŸÜ}ï{«¥ῇq(ògoE†xÐ Ö«@yŠ=žðšvÑü‹& Ú ýÐ$ÎV%à<¿/‰ûD>å÷éšXB}Ñ¥.JN—:B¾DQÍqýŠ¿HÏ*\£žd/; ½ˆ©ßŽ=ì6y1k£Àòa8]vÀŸÛŠ!¨&ªð5¯ýû;ðGºx=°(@ì^8" xDÄŽ Pºô„8ÔsI÷:ü ¹b¡~-«?¥HõQí^ÈI94„Aå¤?°~'äæ²ë¹ä Ù]£_ÏTN½øS\l{Ã/­9ce53˜ƒ–¾¬úDl™U^$½A&qçŠAæ¨ìÈáüúÒRÌûOc¹´ ^Ñdm…µ×— @Éd¢½¶”º8Ëíú ͳÖÏц-£“žSOõs–†ã5„òr+rÿ…c#vú’*¶-ÛšíÄÉr»Š¾UoµÂò-vêȬ5í,´ç^¨µöj ¡ø<ŠÂ|¢À^:±ßJÿÌþᇽŒƒ ½,ÞÃ:]˜ËîójLk¥…ºƒŠG“9š¬^Ö#¸W~’qšùJŽ&v쇊B|â^Qü ®çÅÃü û_üTyVÿ@âµl(ȾÒ—Ì&X,Y7\ÇÇÉÁ*k‚ºËB‡,­ê³ £Ö+`^ÈÏ R8ŒCæk ï½úZÎØåýNå:òàkÞš/ÅŒ*“ùþ{Þm=ÿ¸÷s^7‡Ùd ªíÞ÷Ük%ÿ©$¢´/ðô¢œWs.«‡ .öº~OOÝqÇöÞ÷¾—->»ìÑGuòʉõ¯úˆ%“WøÜ¹s®¨§þA/¾Åf-ÌÛíï|6DåN÷ƒpDuyÄE~„Pø–<&‹ìHÔ #‘% ˆ@èV밙ᬬpBjx.¹®ž³Œþó9+Šäoõ,ÎrOLô0ÿÂäs ®ô0õyîeU&ä‹Ö‰ÚKäól>Ý£$‚Â*YìA0凼ÉvCí†>y_C?BUÜ;’ß¿V^5fb'*Ëìܶ¼ÌYS㌠°—»¤ Î[» ÿ¹¨h6ÿçßü÷z¬®¼—§dg<~yë<éâ4èØÄexßûÞg‡r@\—§yÁn¸í-¼Ï4ZþË+,Æ„h=¬J¹žÞaì/O¿ÐÚs‡¯§OÖ°H¬R8™Ïó+ê‰ËÆ5F%ñK O”Ãeÿ´šŸÃþ‰þ„úòé!vç.ÄqãýÂ_üÃóD¹_Ýß_4ˤí$k𠺪ˆ#µo½ƒ¶ª¹„êÛ~|AØ ²t—rÙ>ÚÞÎö×isÛIP°Ô>øÓñ}@leã8q˜ÎâŸå¾…Km­Ï- °»¡½?˜Ìÿ¹äÍ*á›=Nf€Knç Þs=Hêžå½ã¯î຿žQÔ¾\3hÐ9Xú]ÈóÇò^Àväˆ-½ ÍZ[ ±52‡]}¾êÂjâ9X <)Cþ¹IÄŠε§lÃ\ÄèOe5"˜~,÷¡Ù_8°gªlD¡®KvdЊҕvê$•Š›x»èó4`}°žm›pËZ+ ö{$€ ©ä4>ŽÐÝIw§{KíM—å,;\h7MZy×€jlàĘ Ú_þÍ ›^ü};òì³6'SrùQ½T*?¨—¢^q(LŠ•%ãÃ=/™ôB¿|®Å7î… †:âù¨ÕdÙZ’ &—š¡|>ÔL¢Æ¥l!Xê»Ü*™µžqØIbmJ·éLøJð¨Y›ïë<7)ÿr.ÀÌòj°æÂ´eSLr&«l,,d1SÉñ¼hã– ?—\h¥½í[8Ð:uÍ[ì£û˜=  mkk³Ûn}+PÆNž±=§Ñä-†=—³ñÁËÍ\v_icǰ¶°Ê—^ñiEAˉÀ^Ë)-iNøÀÎ ¸å–[ì]ïzW~Ë äïLʯºU¯ éˆE/€­8I\¼þSÿîWí7>÷7Þì³~žÃ}0gêZIÄ 9‘€Ú“(CHÉŸüÉŸ¸B1[ ŸÆN\…ÝpÕ.vô{ßBqØX… S¹ÞN¦Û@»*ãïK@5tX“çÈAö~”¢ê•–ŸàJ e^Åy—BýžîKõE5zº¾›œ×qؽ¸/£vãûзdž–,þj…ÇÁr1k5“ÖÇ(Â82¥^•r‰jäåP—Ó¸Ôwò,®€¨j!:(8m{y­)(®€äçm‰K 'À— 1ÊH¡°•+¢Î ¬ËiOa3µ¼ÂK©Or_my·¥ß¸ÃwèçàÕ¯JèJ_@óQÏ.¿  8l nÞ•/â'àà¤û…E(ô•Úô6î9J|.mLð¦0¢UPLPó3 è,GíÕ`_J¿EËâÈ™gŒLÏZ]#–:‘õ•ÎÙ@?ç1À…ËUŒ±+iŠ» ¶}GÖ÷„PÕÛø»˜‡­¾dÊŽZUa‹õ•pBp‘½aDŽհΠ”ÐçÌÌúÂécÇÁÖENÔ@ªæˆýÓó' µƒg„àë¿\·ôÅ_n ç•S•"IwÞ=š/Éy ‡²^W¢°Ï±p7Ï;¯+ÔáóQÕÇyý>tŒ¸|ŠÓ¹½t§õAkÞ8g`ÿ+S N—TMTáF97ËÕM׳¸Có™Y0¨E­>YI ‹¢_§ýeæ¦QÄ©€]=DZ’}ØŸ¯uûéÒJ&å<Ûc š± XQQc£Cg8¸ÜƧ3vàø1Û 0Ïq–’¿‹š>zâ¨]Ut)6°ÙÖs ÛØPÕrüºš"khBé.µ …Ÿ‹s¤4D• ˜^ÌIg@T|0'üýï?ÊàÇ@R{ì1Û¿¿Gå7 ˆ ¶¤ Dàoÿáq€çiöZûܾÁñŸ«®ŒkvK}×)†ªK“G§ªorŠ›ËˆŒÛM×]fÿðýóþ—&ö;wnkò¼ÚÕé‹qAë>LS- ¯ÔXYg@Ü×b@¿RêJÚÈOdjàW9ÏK¢ç×ãõ’®:=/m„:Bý*/°Öà‡º•þZ8¯£2[Fì8@e±š­²Ò×8E4Í%»×wu.Ã=¢ì¼ÎÀs'vÎ3äï×É_åE)— Áo7m¡“}®„/Çß‹ßÈ¥RåÖ ¯èx…vIý *ïäz+qöû\ßP` wïYíÓj@ÖfØÈó§/ç9*[Ž!/Ç&Bz?%e&jŸ$à 1 kÙAD<öE°œýˆŠƒŠBN"ÍùúPÉÜÑ jÈþ…)v!Ákß:mƒ¥6ÇLmZšaG@{ÈÀФÕ3g*@6f8´'Ë–ä³ç&­›ÿ©¢³,j¾Ö4`ѯ,Ûg“œæ××5l3­ÛôƒÖÖy3-ÖÓ±åÚq{ôðc÷`|¶‡›×zté-_À%2[ü!.ä ~ˆg>åÝÊ´d‚§…¼±¿,N™‰WœÿÙãõ@)™×½ˆâóaŇª×ì¦*Iy­Eªk¥2€Dš `œ(Á¯Mé6=0=18Z¿eçxJgúÆï^@}‘•v–ÙLdÑåù"4ixë ¾],‹% &éèài€&":Öö‰ÛÛ‡-×7eßzøÎâNs¼fowTÚÞŽÅÀ73—ðxºç=•K:×EY í«ß!Êz_âöBÜ«íGmŽÙ€Rº*Y¬`VÌÌ+¥ Ùó-Ê>:µo¾D¢²%xyãת v´½ _cFf„㠑Гˆ=~%!í&ØBXȰ(Oq8·q)nýnÚH—pí]Jß}#³‹‰Àpótmöë”ÿ2õø‹ŽÊ )àÂŒ½°$¨è ;# ð‹†8ƒÒ¿’±;¡-ºœ¸7™)# ~8…¼´:“è>€} ˱Ž#A´\¢yý0¦S4=ÕÅB«‡õº¯ÙаèW:÷…–ë±I29ÆÎ‰’E;3ƒ½~Î,ÞY{Ê+±Úšm6: ëK» äX›0Á;ˆ¡±Ñ™N»Õýû¯µïL[ue/¢ÀsvöÔ?Ðîã8Ÿcܨ~Èq‚߇Lò)t^9E„È%b% ÷nég¥BsR ŠÇM}ìš ÉT%m·éÞÔ /q»öZa"Çëœe_ÍBõÏ¡Aš XZ­›¿-(Ïå8XCç L£ÓµØMðX°úšZ;ÝÓeGö=c[ŠkLl÷ŸÿùŸ·é‰!«iî´]Wè Úì–ÛÞì@oßÕ65Zªûß]Óvõ{󀀂öã‹/y~pâxnÔ©t]¼² «wÝu—}ðƒ´§Ÿ~Úzè!ßI 꿱‘m_M!"R&|øá‡¹_°Çž=h‡OöÛðؤͭ¸ë蔞IÏ&Nƒ”uøžKmé¹{r¿Ýrï›,!i¬.ƒ8¨(ª<0_G›ÊòkÞª§ä÷ù•ž´8,j]9=—ðÌKéq\¨ÛÕíå£6”Ågi¸Õ;ƒrc`E yC¼œÊæëˆXyG‡~†Û×ÂWw¦ Xsó£Ö߇%7€W Mp[¼æÄºµ( ÿGðuXMÏÛJx—¦$°riÉ%M÷z#¥Dz(XCÚáøOá Ôê ½øZ,7îôÊŠîàê¤ïQy!ª[m¯å~Û²ÿùžŠÇ)ŽZŠS óÉ͆ÐíB¸€ S–Á>ó“5 Ç1½è+$&0 f›ãŒ‹bì÷ÀbŸÍÁþÇäwñøƒ LÍ 9˜Æ`Žk±;ÂþÙ«L£•?b‡»J¬^¢?±rt$Í–Öq¸1²Åa‡ÎB‘>Žø™³ÓvåÎ GøúÖäÁ‚VÛVxÆ:š&ìôYŒƒñp}'fì…i”zAÒxÙ+^Ç…ÞÍZïìƇy±‘jÔÍõ–Ó@.Y.̳óÞ2“Ñó†²É‚qe*\ÈëQq<éQ(Œ_\Æ“‡ï͘gˆÖI¡W¸¼ ™B8î«{ ½+JmªÛ×€Ý õ—½ô¢ýLXxSˆæÙPQÕl³3Ú«ŒÜÍÜ9LgVUrÂV²f¡ŒJK™øEÔá  2”×Êk í¶›n±ù?û/nŸ¿ôò;u¼ÏÞs;²LŽ> \À¯·÷4 U/bQïj{ò[oã=’)FNòqÙÌPL"J ”|œuCž¸¢äCªÿoÿöoÝ€L K1P€þàƒŽH| ­…bÛËÐiä‘.Y°X@6è4à%jؾ}»ë¨â:ˆý¯¾)ýÁ‡^°[î/ A@`ÉÉló 7]ÎêÜm…yŠe)ýb¡$7%|íà«l«âDøäŒã—­ñÄKƒ·«EF^åß{šn¹Býyà¯Ä¸.ê'”S]ºåòr+òy‰8]¯ó>ªuºÀ«zD„ì²FÎdGvÝåÈkÎFž¤“Œbi¿/Z—À¥z-€+qtIYËšÌ#‹+J?åÛ÷ó¥>Gœ†\âAR¡P]ªw}ˆoÁ5 Q˜éÛ™§Ù‰ÿyøŸDÔTúvêø'®¤Ó»Pÿ]ÿoKïú¯Vðmž‰†m°#”EÛ ÒˆÕÊFû¡ö‡IÞÑYÌôÂÊ/çˆÝEüyŒkͳ]!Þm‚óŠÐ˜ŸçøðBén!y,L¡;Ç@Ԫ‹¦0£[eµe…629ŽIîf”„‹Q˜²©±:›\(¶tÕ"•­lå›B\YlÛŠlKÙ0€¿‰]œ*på¨ôtØBñ¸rzéÔøi/{ÂúžaG“3}4á¿Nº¯Ï¥+„/ö(š'a¼«\Þq“LóW2Ä „6¼ô²*G Ôéó0Žãkº8lQc—ø-ƨ\kŒ(}j÷˜Ü"zd)4ëe5¶5RV8gýSo^ 6¼“xÒÛ’zêæu¯1Ê|Ÿë%Þc12Ï©I@AþVV†6/J;9äuÂìSl¨¨Ü†¬S¼s˜Ï­k±0öD  šæC·]ºÕ¶¿år+ýT…}ç;ß±-%7‚#9=Þe½csvÝåWÚØÛíÈá ð(1 Ô‹õ¬¶†H¾­®@)|üã÷­|þçî±òÏwÿ ¾ÅߎÁzÂ=ôÒ)î€âàÆ"t /D½™êæ³€S¤ï¦‡Z“ÝLïÊ¢ê;ÏÁ©…f¿n»þÔ…!˜ã±`öÙä9P²daßëp=26þøÏ–ß"Î+¯@'%7ashí"’,„.’¹$=c#àG•åüÆ-höc 'ƒQº¢´µlŸ´Ôa-)/m³’†£œÆ'‹”ÍÌMÖ”KS6›ê·Îìôc>¹š:t5[½£-#6Žýÿìâ)æLöM,|ÍŽŸAD7ÅLJà>oõÿNƒ¥Ó£%ãÃ}˜_Ì“e.¤{4?!_ˆWædšD¿W|٠ǨæH‘/ Gì}ô:0$—y–[׌Šøµýà Ük\¯±Žzýj‡,îòï3À™²ý×À·—Ͱå.‡|¦ 5Çöçb%k‚C,Ò|Hmå™™…íF4"‚Iäгs³°È¥9Ÿ²³ýãLxpv¶Ùè˜÷­­³ûï¿Àq¶ñ¤¬rW=ú‡o醻á†lÏe—YûÞ[lñ”?ëÆÇ?ñ3ö¹¯ÿ?þ½«ÑžCtP]ãÀ1)ïGà•"zb-a[žìø~ík_sB2'ÜÑÑáç |ë[ßò”6S/ƒúm è«=‰Ä€Dâ1‘,^ØóM×_a¿°«œ·R΢Yü>ßEµmÌ9‡"šFð‡°ž-Ô&»¦|bê-5/¡l~!QçP¨d´`è#¯cee´ú òžŒÚ^jìüPT%”ÿ5rêO­‰Ì°íÀ¤ì‰³EV;,ÎÑ" n1˜Å×Y[ñUB¨ õ#Q@DñKD *œ‹ÁîºK;==¢úØ…ÔjQàOƼ[“|Bö"wO}Ô«þ¿ëj®ë¹n³¿È”XGhIgøt3ù¶…"ã C‰n'¸ªÙ”Ÿª:ÃÒ G£Ñ•Ø÷Åhí§¦leÕl®DÄ–r5õÓpMÊaí’ä NaFó@ÿ¹ ùð.ç m" ¾ Éê+PÆE§`º N@_µÍOZ%ÛNs|x"XþÕéQcS’ õÌXß”~Éq+›>aCc3~³Âé™WŽ“ÕâVÛÌ·ê¾\ð£»è>š!féÑ=¯ ĞϵV‰Õ柧« 0ï=¯Ö$­OòeÅ*€îÀ;o'Ð×xS%Bî´¾qŠ,»ËtÒlø,êÚb ú G”?±y€"£ìZ5O ³Þ´îõ,äºÃÂÞ¤¶î°×‰ï ›…E¬Íÿ¹ÜˆM°/·´„sº±Í=ÍA)¥Í`U@)š¹gg0 æÇQ¹Ç_Üou,vf·Õc­+վŞï9d÷½åö©ßü»ùæ7ZQÚÎãg13œ,jÄ æÅA ²“(CÙÏ"RÁ°"Ö6 Ú–jöGŒ¡Ø0fã<Èöê4 ¼ãFr¹Ð ‘å?k@É\~$%ò¬—H~=ó“2ñ!NóE.Üë5(ìó@H÷q^Í_wòùŽ:¬)rèr ºbáKÆ¿€-™ã¾&qÊ+'åBg ‹(§êBW’\…(¬¶¼sžW?QWÑ X”šëæu¯ `>;{%…]ZZî@Ov·sµ9ËþÝ*(úyØÞó9Bå˜**Á&¾ð¿I4tG§ PÄACÍ÷öþä[ïµ/}éKv=”ÿu:Ã¥‰›Å$ðÛê¾…ž_û7ï±Úío·Ž+:ìÅ<k½Ú¯‚€0Ò~êþ÷[fÂ~û3ÿBÝ8Må{5ê—!Èü%å¯wÞy§zì±ÇìYŒÈ&ÀËu¡}!â@¨-éDè¹d³_“¤ç‰}vó;nùB¿JK ì­o 0;r¤mi“9!ÑT9…¢i§>)´ðWÎàB>_ |ÖRŠÅÂãýgŽê•¤›©NLœGAo6Jœ¸vêñdêñ>ª>\¾a?«Â#åµXÅy•ôJ]h{Ù»@ü5Ï¡4­(¯ÝSB}ø”‹Á7)Q}ñ Ž2)¾‘¸pðTVí®á<-äó^ ÌU]0ÚŸ6á}V¿5¦Jì¥>í^4¨¶—¬Ûé>[ó [ávÀ1=ÀCÆAV«Kç­<7eã «—·VÚáØù@A8›ž`céå>”ùæQ MPYŸ±ºl’­Œ•pèÏ2…2¸ˆ³è¥È[_ˆYqŽë]œ,@ì2l=6´ç Ê“Œ_ëžøPc2´NÌàDPÕåÛQ8ÙnNfmäßw2¿ÊoЩîPçjE=MmðÆ¶ “^l^´îCM6¬Žsê'hüø&Ѐ…DˆØøÞ¢ÞOri98Ë% Þýªo)à¯q¬£páÁÀg~Qƒäþ)ÚˆŒ …=>ˆ¾êPio€hÝnàïi#ŽëJ(ÿ«.øûƒñØÆ%*­“K”ú5\´DÓÃt}° ÁCô|¥æêQº²“nŸGGg9>ÂjÞ@~hŒ3-`³Ëßškg8¬§ñß"½Œ wµ`à$ܬbk¨F ¾ãýŒãJ”wÙ6XBGË9Aqz€CE5€¡`¥JIæ1;Ñ NBŒwº80×8t·ò«éá·<ýâÀ?ªmõßPçꩯ8vå#„ }¬…›WâÓ€Ï êðo^OòÞÃü äšZì€Ýs¯¥麗Æå ‡n‡gýË ƒÓb±¹Ltzºº¾u  “G¤™?7‡aÒ:ß±U\ÌçIU`É [ÛÈB++¡DéC…–W5²#Mu°÷ž³Ã¶³©Ôöã  NIèµßùúïYE‹…8FÚݹÅvagÿïÿ€}îóŸµ\6ˆ ÆlçV8ˆˆ‘þA»êª«¬ƒ<¢Œ¬ê[Dùgk§NðÏ-YUF04¹µíÕ +¹]]ìbèíµ›nºÉMøB²w ®ÁzꕘCl…Å ÍùŠ“ÛüŒûwÞr¥uîCÚÞÇàó,¶PJžº±ŸÈ…¦šG©â,!Ä&Ÿ`óqañ È@ð©M“69y—ª=R;/S¢Û¡¼¢BY÷“m&ò{¾.˳2}÷ªc=nqŠqãüÌÜ–žeì¢ sþEAÖ†uÈ©við·àëªö³\úRZ ¸ÞHú.ü*®Ar<ÿ¾‰„\×Ú’UÁ¨Õ¥2B°œêQ‚îPø= »Î½Pèaµ­¬ÝíÀõ’.xòg»bx(êËÕø[ê­@õ2, Àm  _L3eÄ=C—Ù°Ãó‚€eLƒ t¶ŒÛ²¬wÎXKç¢õž± üòÖÔ¶é1 þÌÖXyÓ Ûï`å×c×!Çn—ªæ †Yk¨å¼¾sY›«šù¯Âø œ$ÊŽ¢l/H­9={p§Á%¿Xˆ ~È#µ¸dúů´üêOÌ äZž¤î$=™ºZZ˜Ë*“O' ø|ûÌ¡yÆÂô–z;ÕoMÖz~8 Ȼ抜B$ݘ²ò=§ÃŠDüëÔÒ ™¢1§cûÒÀ`Q9ÄYÄCî蓺™wyÊ?<€R–‡ÙÀhu¥š|±ÍЬ=¸qä)c…)Tvåø²øWÌÖ?-I:é\ÎuÊÒ5°ûá¼,²ÐåÀБçÌ P‚|ZìŸÓçlZ0jfÒZ.©²–Â6ëëíãøÜ,o¥íôñS¶£¹ SŸÖÑÞ»oÁžúñ#ÖžÛcÍuM6òÔ Ö×}Öž=¶ßî¹ûn:'bïn›9sÆþ—Oþ[û·ŸþEï¢Nû•ÿø¿ÙÃ?|ÄdÕo-÷j!.›‡û¸:LHíÞyg´e°³³Ó~üã;7@@3p*Öê—€¾êÀW^Qæ2 ,.€ú¬SSLŽÅ}öÙ?ý´}å›?d›•˜»mvøÀ#PGš€wne’øTù(ìuiaÀiš—GB¾â’yô¼!N5$û¾îÄuxœGD©^—êX‹£‘È«*TÊ—õCå÷JM”Nqi‰ è°Øß©4mƒü.BU˰‰ÛùgQ0 å‰8“ê[;ÝòŸXù‚ÄŤk¼J¼%¦–Œ@9À‡÷®EÕ/Å‹b?GŽÁ…õZ±…pª•1¸ËrÍ ö€þ‹ˆòÛø)ðñ=í  ‘—‚4¾ >ƒ~w`ßÎi`rº¨‚Å~‚®O’ .!¦÷ÚÇ=ðÛZ~Et9E7Æ)ÞÐ>mç&è)ÛOÑè;>g#ì†H ÃèÇÚžüfl¬OïhÊÒõZ7à2‡§‡ªl¢dÆ.ÙÂQʈçr‡m´ÿ˜ÍÒB'òοlþ. è=— ‡¸•e’÷ÉpÈÿ/ìkŽ^²¿]ÿu.¢Ñ™.JƒC= Q€u_kSˆÊJÑà(É{² P s˜ùÌù!5;‘ïµ³g·p97öý§1ŸÛT_iÇœµŽ›ëìÀ#Gì;?À·/°ãݧØ&ÔºÔarbQÔÙÙûÍ_¼ÇR-o³²ú ûî}¯ý¯¿þŸX¶Ãhßu™]yùn?qp5.€€ƒöÚK¾¾ÒÐË&ÒÁœ° ðå/Ù®¼òJÓnÎÎN?W@²ü|ð‚MH ~ Ø PÿeôHÔ¿€©êÏÁ2ù/¿üûö~ýOmïîv,a·ÁìýÆ·g‹•(ù;qÜúâ˜hjÉO T©xÍËàGã!¶ZH’ß–ÐhVRöüº““׳kA’S™¤ó:ijOéÄ'‘Ï‘ˆ‹K$kzEao‹úóï@SÒ—mY°C}óÐâèÅHÉI2Mö»£ÖÎXæ›HHB)ð ŸÖ%Ê]Š‚@\¯ñ ¾Œóø%nAô}…@Ô5ƒ€—™{ÅhÊ×Öî¶ÔD“uî*Çœ6*|eÎÀ±ÛAŠèͬËþ··"Lj«ïD»"b÷“à‡ Ñ@Qq´Úù§@"°øm§AjX1þ uNö‘ fÄÕ49â+O>X £–­­m¶röï´ +4‹Ñ0ìðMaár,Ãá?(£ 4ßø¸ížµG²® ðåßµ¯ÅEcHáð®éVæ õ&Ë&ÃkV´y’´²—áñ¿2¬8”-S¹Ëù ï²ÔÈí¼Q>î눡ƒ& õ1öÑà_ô}Ȥ€/°…3O©b¾T~JâuHsŽXQq)\Æ»è$Háü²§/N¤L"&<ÓŠpºòù7iàõ‚0Å\|Cò= ³“€ééIrj1à7‰Q‡r4ÒaWowC@…ð‹‘d`å¤ e8dz»gÊne‘*À.ÀÐô¨µ@Ù÷M°zð!§êÅÈžáSÖ”®vö÷U׿Ùrg¢Ås47 P¿Òþꡯ[ zÎ.ê±^”åÛìšk®qÃ@ýèG½«wßz½°ÿ uww'»îa)ØIÓ^Àv%äíèuªK@PTºÂÚ 3Á:ÕO6´¸©?¬Å ~Áð0;(裀¿úŒ iG€¶Jƒþ‹_þÀ9û¥ÿùCöø3¼«?±„,²7ÚwÏï2Y2ì(NÏÆCä3èy4…=..»2O”ÍÔDÉxù&&Dª"ô>Îsjƒº#Àp^j÷K¥½?ø~¨P¢¯ç—ÜxLèCh#øåUvŒ€J8fv‚ä`‰±N•Á¤Rà` RØ ‹Ýé. WÜJXþ©c@Y¾[lmA q´DpÆ£Õ´4#R(@nŠÍû ¤ÿå­vbj¿´ÞªË­ãÌêÒ”ÖÁÛ÷ða´±€¢Šc·¼Iû Ì~)Ú9â[ ü9ê-|"õ3ܰÝΑ ,ž¢•”=ÍIt-Kž)| îaÅ“x8XGÛ1k{*Å6Èò+šÇœîL…µe9!K{Õé.›å»|ä»÷Þ{Ýlî™3té‡>ô;|vÂ>ûÙÏž7´K@Y€øpÎAÈTg Å9ã(4å#–ãu걸áúëm§ê ±ôƒ¡üãö‘|Ä}ôQG”€Jh_÷Š—A_z饞,„ Øè³~ùKv÷Gþ'®s?vªŸ÷/à²q§~ò"€|»üs‡ÕEK4aú–Ëäúâá°øÈ_Õ%ã…XD˾džfb@Ú ÑþŽ@þ¼êã6•ßë[³ç•ÜP„#7q‰ðÝ7‹ùçÅ :[f¬q³!Ø`32ç+ Ÿwêb &ÚûÒâwý¤²³$‘OŠS”õnYмi,l–Ö`ì—ýóE%ÚuUU`mÅõ|¢zT[í’V¸eªøC<ÍØ·BÝs¬½¡‹gU;@=v¢:Ø‹D¿tZbÐ ×@WíÕ§ÖÔvåÃ~›ì´‰ŠcŒã¤9ˆgk2»r±Ã¼à1Ê)?^1m5Såô#g ‹6µ0ìVþúXüwnažŽ,ZcÝ´íëßn‹ ³5•},ëý~*èLögmÓ“ØA.|a…£1–%Ñ×\žG9—Ê)wþ«{Jô“Œ[-ÏòV_» Æïkí–µž[2~²×Â)zŒÝÎ}ūؼ¤H)3|Wq,ŽèÂbŒçOå;/:°+ŠJ\S¹5°¿ÿà|¥aio–1Ù¸•*,ÞÇOý¿ì½œeÅuç^çœãL÷L÷ä! r `ÐG(®’wµöÚ–½þØZíß+[¶%ûoy×öj½òydÉZ£,l$aFÀ`òôô„Î9§×ýÿþªn½wß›žaž±]3ý*ª[÷ÞºuN:aŽo-™én+œ:Îçºg=»aì®”Gòþ„7åc½qO^†òðî«L!».äCì¡ül iÅ¡¯xY<Ý>^w–¦ÝÜfÜú²3š¹7XÎÒûã×Á}M‚Z‚vì¨}cúÉg…üÝ«Ë_o–|*À)Í‘ysµÑ ˆ  4êåùhÌã1V#Mß,>( ¯Ì½ZŸÑ#!‚ :ÔõÙÎ$¤-µ{à"¾´Îð²*…äe°ê „9;Ýç}ÜœpDYÊÎd”3x t¾òªmpro\ØÙgQ¸´í. ‹ ð†LAGyųvì™}nw>´e¥-­šÂS×}#ðÔ.&ÊjIC6ÛyȆÐÖ.(ãC­-«ì+_ýŠ}þsÀJÜ`í«ÆÐ3"lp–‚Xèò°×»÷˜ëëvºûbÕ‡°nÝ:{ë›ßbO?ÿ¬CØ*×½Ý ÷½ ³ôÖ5«í=w¼Ý>÷¹Ï¥<êhaddÄyܳg;Ê ·mÛæ\ä2 Ò×8u-©ô Ùùë>D8AêžÙÁŠl6±ûyû•ß¾ –Ú+ sŒ[×Èå^yYô¢?–e®í©û@H…/O±‚tA³kë¿ÏT1‰øâž‚@,£û×_@®áJ®Ÿ¨<¤ýèÒÍÕF!˜8|HGó=d]¼Äæ`þ–éO^Nâ5KåZ%Hæ…Ýü’(eÒîžM ?³\#ŽðßCnA=jâÇñÃ7¿ˆ*3öcæ°#2ÒQµº¸î=Sž—âTbŒ °ŽqPuv‡s†X˜‡œŸ-“NïSg4à Þ/iýyÎÿróËÜÐþS,ˉ vþ¥ +^îç”Çã“Á„LJ]6ÙÑÑéÏñò^ðµ)dwÁ½‡ÈŠÛ0Éç«wuÊü–'¯æ§fþLåὃKJÊÊm «§Ì<ª˜¤¨Êv@2©²¬p²ç˜¾ÉäÒâìÂŒÄTÏîpÎúÙÑONö ÒSÍî¡´?hä Í)#n!G˜#çbžÀ0Ž’;‰Ó¡±akž«²•m²ÎbRLÝ+Õ gýuxY€š¼óηâ_`ʪ*«íÀÄq«\Qdí›6ÚwÝi=]Ý–·jup üœà”óîÜKäàño©Üʪ0¢5ÎNízê¯b¹×À:=8TÇBØiýc€ì“VŠþá!\èBhílfÎc’w»Ã9CLOŒ”W7ϱÐÁïçÝð~@®¥y¼<™ú™-²ØõAíc†ËÝå œ@Ú¡À¢â2ê0S‚ €ùNƒÐ moi°Êc¥V BCˆjta„3öyìÔØÑ==víu×Ú=_ûs»áBÀù2¬Vã@„¶¡É¸ÿŸìðÖ!˜T+êm÷ñƒ8ªpª}IÔàz†á"ä;Ñ«jÜŠ¾ùÍo¶¶¶6“Žþí·¾É~í×~ÍÆïß…Ò|ûë¿þëB²½ëoGáyÉnZ{™}1ñàpœ±ëAgóRÇò^.hn‹aâJνŽá™³ÿ 4%T'®kÄÍ KQþ.¿ür“IaÉèXà;‘çAÁ !J@„ƒŸäú’ĉA°rX¦a!*OHÙÃwµgö#yõZ<òO/°º’ûD#0åÒAÈ0Þ‡ïM¿éV¾߇+MWé!ùÎè'W•»ž¢k¸ëÄà®äÒ¡¯ÐÑ+ˆu?!¤S¡„»ŠêEÝVTÕÀVnƒ…̘©&«o…JL¢2ÿ––ª¬$ŸÄHÖéê 'Y@Sd¼À[«ìx7öÕ‡ljnƒ!í?~$‰A¬|» )ßW[ÿ3•Ö¬…í^hWÀÙEöÎz9˜»“3v^㇔]~‘ÓȵIhÚÙFdf`ÿÛaÊiƒM.Îç±Þ7±Æj±Ë?Sƒ—½6´Z†ÙAˆˆqÖóù)\wsÃÅ5ƒ¶{ÿq{|úI;úâ3Ö;ü‚{¿ñwèŸFúéÄçŠÞˆjô^ÒA¹xiºmº<Ôg¶Ìî)ÝçYž ·|¦Ã ·ïÂɳæ‘5¾y!}¿ 8Ó._mø¥u1qœÁ+I›q"óAþGR,}´|@:Ì©ÉêmãßÂ=ák%ô'8b‚øZâªÝÜQ‚+ô­ ‹If¹³lÎÊøœ!榇÷´L/Ä/z \×àðGjIØÔH@cƒl´©dy²ãI;ÙO.tƒSMGJcžœ .p¶YgÕxî’Ũ–ÚÖÝÇâ‡*ÈЮp[‹ìÐø°u>w!< âÐO«Êfmïó{lç“ϰ¡ AþI¸ ²C:›ÄîxS»}î÷>ÇÑ*HÅ…Ö;4nùtÇý˜öW^iÿøÇíƒo“Õ±]‡h©*ä•# íªƒñ±å y†Ûßû~+ùâ¯:Ä*Dþ®w½Ë òéÿdô|r pʯÞl7Þt£ýýßÿ½ËëG,|‡Ð‹›‡á±Çsæ„oºé&§¡ðüóÏ»#qDT¬X±® ¶@úqÂQ^ÞI8ÂH]ì »äx³´äCâŸÖì¹À‡Z_ê(ü¨È­Y.R¡·ô«”«åGmƒêž›cŒ';8X•s¿.„ˆ)Ï( u¯Q¿ž›zGï-έ²Fìäöb㼺ª‰ù\‰¹Û9äìð‚G©`±ÁÿXUZM!jž,Œ££CVWTeeÍáxïU«0’U0†Î|õ=[j«ÛË‘·®AéþÃ`e‡?¤›åUAŒµ™uðXnéW²›?š̔¶¡Ug´!ƒÐA Ã=Xí›îͳƒý¸÷­Áêä¡"›¥® ãEó¥}öГ°÷Gï³}½Ø r6î»çé†7¢X—WðÏ<”ˆP«¥<]æaý¯o­ßP¯´êBÛçc½ñ8¤ }p!¿\`B¼ÌkX¦Ëž*Ds:¢¼cå×yö~7H?2Íœ‚9K 8‰ÖQäÀ“]Ãþ÷öýÑ^ÁV…vÿ2 àŽœþ?EÆÜËR!‘ }ÑÀiÈhâFÁíüI»…B1Ù‚<Æéøâ¼Wrga8gžÝôbr~ 7¯ 1,Ìr453Än¸Ý}m¡þÓ˧œ…§$,Óø ¨CV`víÚh ÃVb|^u]´å"'t70Ðo?øêq=ÖpñZëÁQÒ§çŸ~êàñÇŸ°ñl<Ù:£×—’ÖQÁ4gUl^GVšåÌk ©™ý¶{Ç‹QH&ÿÑu´Ë—#!¹J–ÚáwÜáL«…""âÉîç§Í{sÀ¾ÿMiÙåïGï;•eæCMfìæŠ›1¾\yõ’êI !w —Ô$ 0™‡ØÕø÷ B¹bþ\˨} ôŒ“áù*ö÷®îuשá¦úÔ;ŸœdgÎU>v/Žw‹äþl9ºô+X‘•)^‚pmæ»HZ‚ÇFàqàÞV‰Ïû‚ «/IZòK ä²rLe£>ÛÞ˜o—€´sà®Ý|óG[…㬠÷êVÌýî`E–%o=}ÖÛM«Pýcé;)kÌëÛNÎ‡¨3”_5i=c;mçž~ûÃïÿ_ûÿþâcö©»?j_üñ‚ü·2vdÿ £{ôï#zMÑPY¼$zö”ùyáë|ËLXß¹êUî!â}Q˜Ò×IW„vé’s:•lCjÿc–ÓñMËí¼—³ý_ãÉœýÈß=ó¡&‚{°ò×Âë¿kÃÃB,ÿRw„A>„¯|É,pt\êðƒ y¢•î\ÿ;‘3Úe½ëøtpU™õežõ€ÜhdæK©s!,-‰«bñÌãÈö—£§9Œ…Ë"XŽvñ…8zà 1ßbóäaúq Õ?½Ô"mEs5®i±'_ÃÕgÂ.]u‘uÍ>‡_ñ*„ KA†pèïM×ß`÷ï°Þ§ŸÀŠ g§rrS8‹ÍðÕnWþÝï~Û&gñ&ÕÙo¹¥X„ë#’ÙYv[CcÔ¡`È"Ü~Ë[lçK»ìø uïø|è1[³êmV¼®Þ&Ÿ|Ñ!\ ÖIßþƒÒ}àÇ?b!®°}¼Õ=úè#vËe«íg¯ÙÈ¢É=‚ˆ— +Ö¯¶œKÖÛâS;PåâÅ‚€ Ó+vI±ðuƯ~uä îÀ÷¾÷=Çþ—%AyN”l€82<ÔìOËöσ®©<àý®oУ?-Ù+¾Äð*Ò!N—¨‡läàz( !Ò€x5®€pUêÂU¯2…xWð*ü„¾¹HúŽ¢ëew¯ë/.UÊ\¯@ ph¥­Þ”g}‡á€±;ªÎ¯µ.÷ô®Vpî_†íþ–Æ2Û‡D}ùBjžóÖú`[Uš*§lv¼Í6ÕNÚ‘„Õ4Á]èP7DnõÈn±‹lWD7b%-ˆÜ\`öˆŽ–`íD˜£iÖá¦Zèâ=È\çËüoȩܕ«ÐâB›ÏŽ_®>þlË£ç”\ÏËz3nu9×·ö³m€§=žD;{'½?å8½ p3¥J¼¸È¤”1 íðdS²y2 â×qã"¦~ç0þædœà Ià—°•!Ó“†ðú]ì3!É‘ôá“¶;‹*Î¥#íø‹ .8õ‘Kð/sIÈþ|R% x·h²Ó‘î1†K¦ç1 TTa Ê3àó/tYÁ]EVÕ°†]Ê1[]±Â^쌶ȰHÎÃ5ægvPÐr¬ €3s6;‚ ÝÏuÉ 1¬Rv:“È Ìës܃™¹èLì `£`õ8†ƒµÀRÛ{Ï#vÕW:û#[Á7€T>kè%m ÷n³nÁœvNÃE½±‰xÄì0kq!Dð2+šÍžžxÆ~ro§xĶÙnCÀӛџ?é§Ðwxç¡^îiD1ïÓ•+Äó!oç¡üov›xû8\<½\ÕÇÇ®ow†é0ä嚪n9øŒ2þâ:^Î- ýÛuKFíë‘Ñû9æÌÏ\áUHÚkº§-ËSÚá„üÅ}OBØêSvzáîl$/A@IªâêÙð évŽâ8}Vn6“‹È‰I“·q” EÒF!es|%¿ŒÄ}ë¬O‹IHâ³?ø'vöÓ]Š{¨~QÖ
gíó8z @ˆj1Ês“8°áˆ ¿àìÐó¥ ?r'A9L†áÁëØ}Ðzq˜sÕ/e—Sg‡»X Ó ñ$„E®B'âÛ»w¿]rË6Œ5ÁÁ¾Y«¨BãC2ªsˆ»¶ºÔwXëÊl—ئæµÎ°•\yšBÊyëžç¬»ã˜}ô£u*vS Ø—a’µ½Î^Bï>¯oή¿þz¸pÎ{rÇ´ç‡&E£áú[o¶|Ž0¤>øîw¾ÁG&.»½|ŽNž~ü)[üá,NØÏ!_Îí/ ¾\Ü Šõ/ß2Y|ï½÷:×ÀmmmnLrhTSS㥗ëótêÓÖýbëÝgîÞmXÿ<šP­R*õ0ék¤!=*ñõ®Ôwš rúˆùœk¡U„?—VYT¯2×ê#XW'ØÐ‡âW)d#~u¿ÒyÌ_æF¾XœƒxÅB¥4¼ùÖØ„S§$ê›K 6ÂŽ§0õ;¬räU<šG`ÂòšÑéÊa_ÀvTI*q¡»ÊòYOs*F­ a¾M—Ì8µ¾£³5û±‘’÷Ô›Ú—aÝ”Kö•%S¶çàãöƒoþ¾ý»ÏÝd¿ôk¿j_}ôOì¡—ž·aÙòå©ù§’ýlBÞÛzª*ño‚„ûâ}¬_•û7à!ý[PM<¨‡åkBï'¯÷ÒaŒ!úå!õ¯0Ý.×üTuËÁƒÉó-1ú°÷ÿÁr:ÿ¯åôŒ§òú# ïCŸâˆîЇ~qÙÁ¾ÂBÖYø¯ù°ò&ÀR…{ëe•P¥ ì’r&«óˆó¸R¨VÈÌ镺 ¸Én>ðm¸cý»ã€ØÃâÈ?‡ØÍT ÝûÉä|rg{8·8KKÇSTï‚ŵÃ?y¹P{Òçä•”1³°‹ûß…)¤ŒQ,.h&†äD%¤¬¬ õ?É äs$0a[Ÿ~Ún¸¤Õ 9¨m?o}Hÿ"@²¢o‚,š›7ofKƒísÊõÀÊAè¥sÅÎæ“fµØ@Äy]kƒMîš¶M-kì%Lçâ .„Â<'½Ï²º-u޵.£C»;ØBöuw¢Ã]äêñãÇí-oy çµ:³BÍj¨Ï¶´_ît‰³à÷ï}×V¿ëb_7`Ip¥ ïupÙ?“èã¿ðâv›„E_¾ßkíê%qºAÂ~â„c}ûö9‡òcÐÞÞn?ùÉOÜ»Icq$?ðÓx¼ÉbÊ{ߘÞw„k]÷¾< -¼RÕñ…XÇç…0|Z°”ùÿ®¥úNák2‰(yè5…t©ÏFÊ©:Ÿað÷¿‡ÌÔwî:dÂõ•ŸÀXþìT§µ6´Ú8ó*9Ë…E5†újÅü8»t´^ ÁÒcyxÂ+Äo¡%gAȸ˛^g#SÏÖtÙ² øÑ+ì¶6$ª+0'ÜZ•oÇ1÷7udR>e8Ó¿j5My~ÇÙ|Ý» ®Wâ[¶×Ùûº_àUȇ…aÔ!÷ 8Àø”Gçz?¾<´±Ê}Z¿JH½{ŸNÃúw®–¾R(IÇËÕù+¤aâ©2˜xy<ïã5N³¾%’²Ó¿Éƒ’Þé%áwþÀä-wšý¿ÿù§”{ÒùæXàØ63è óÚ½c {Ùâ©Iˆc}?˜ÃžÅl»ää0GgÇ$Ñà*7LMJ–@«<ß„AGÈò±áC콇dˆ#]o¦ˆaGøç0?;Õmeµ¬y CèÓb9°ÿ+K+lÁ¿’ØžèúüyÈÔ`/`ö<Èýÿòš$e˜@-îpçç?vhÿ>;ÝFkh*±¾]ƒÖ52jím8=[²~øÃÖùä÷lžfœ£ÊU š±+aé?ñèÃö†M+ÙMY3‡Ÿ/ y°®±Í±¨gg0öÃŒ`þL-`&u}ä÷~ÓÖùðm©CÀäH'œLª2iŸxü‡&#<]]ÇíÿùÅ÷¹éSÂkûƒO9„­óöûôCûäÛ/Àq[‡µ­l†" dP׈‰K®ÝÑqÜÊžÎ`Ñ ¡‹›p&AˆF×D€ŽvíÚåŽ$ -)7@uâœÌNÁË]שêCʤŒ¯’?_£_Ÿ‹C†RÁ*ø:×Ôå|>…8¨Hqö¨JõGþúªù zu˜g× æÕzºžþRc%­:¬>X!Dlÿ(¼ƒ ØœùØÊ/ÀEðÐpÇWKVÕŒ±àâ¤åLWc¾ÓÖ¥EÖ;¹Ê6ä⣠c¿èùô&ìâÆƒVÙŒHúîÁÐ|«_‹îþÀ¢]°²ÄÞºö¨½ðô!ÛwdÈžïü‚·Ï„Gí柲Fé‘¶«J=’×àYî„j};>Ò¾eè7õþ2ž‚¿ ÝÒíB¯¾<ü؇òû±.ß6Òñ8ŒReñ´ò'»žê^&„!,±ðEœí _›¡³8¬]÷j N6àð"ë’›‹Ÿ Tª²ä'!*Ǿgæ„ÉrP\ZeÓlæ– “Ò ŒìõÌB@sÎL>—·Ä{‚H˜ÖÆ1 ©W—ýNÓy}‡p~ç“s²yyö‡3Ãÿü÷3ÌÃÕ›•$‡_yà Ïy<ýÕ Õ?™ßÔ¡’Hñ—–Õ9@½Ì!¤¢+П›c§À‹Õ+Ë!žCª| ‹‚¨¾-öÙ¦õ¬xó{î©@fý ÏÙ¾çwÙº76¢‡ˆW1¼ åcdåP—m…¥Y3h×]Ò΂šá‘k{»ÚêzL¨.$m`J“ ¶Ô9ˆW5è㪮ƒvȃc8/B¶àÐÖÝpœØ¹a’õ߸„£N ¯gpÂVä'mý4ðlV±¢#‡Ûn{ ¶JìÐΧ¬¦T3òÄ I(‡DWüû;ìÁ?jמïr8§wBv"완  "âi¸']]]ÎŒ°¸íííN@Pö P=Á £ƒ3½†FFî0ä=:I׫<Àøûð;A÷»ß2ÀØÐŸ.äêT‘*T§DÏFUÒL8¡­ë?ú‰`}_Œá Ÿ«z ˆ=ÞmH;BƒL¼ß8¼Òc¸Æ^‡‹>\b¯]9a;^ZºkMìï³’ztøR­)€ÝÉÎ?¯CRxóË/[²±ŽIë¯ÅÀUí”ÓǪYvsåv\~xçÖXMí°Íp´5|ƒ<ÝÏÚc»öÛüÄ?aÁãO©Çäî<nxz á©©*J»6FOÕÝ[Æó m§ûJ¥\ûèR©(U•¤IÝPÄóá:!0¯E¿îö¯á¾À ObòM¬7Q[qÄ¿üôþsqé âO.ˆdõ¡°­­I –cO™lê °3=!n€8@eGçˆÉ‹ÁÓî_åÒ A‚£ã%Ú.âÓ%z­šË‹K‹’ ÑßYÎ)`a~v`6G[~‚_ 9»…“Ž?õœ½ÃÖÄÍoÆy¦8³×ºRŠÎ{>FPœ;à‚rËÅiÏ z‚«q<‹g_×!«o®@ˆ©ÇfúªmŪ ˜ë-³ƒ{¦l ÿ¦Ý‚À#L¦„ s­!¿ñCý6_6hs ÎaUj©ÿCC‡­ì‰G»à>À–‚¸hi©°þÃäaÉNÞ—ǼÒíŒ&13Œ:TgU^™çN0nT¤:_²æ‰|çHàMù}tßGì¾þÀÞû¾÷³dî³a<§­YÕzÒ 6€ŒƒqŠpx¨ËîB~áJ ÿag®ºôþåö÷L‚ž{ &d•PZòšø¦7½É‹èH@Ü OÊæ‚Ž$TxºAG!è»Òw¾µ°zÄÓPê[ùœG> ÓH\åšB`ÇD]¹(Âj¡wžß¿î?䌯Q_qÄ“*=ã„»†œoêæz”ÏF˜!Ÿ3;f]½ù¶re=õ|ÂÚ›æ‘u:dÃUìàŒØzl÷Ï•ÛÌÎkXÇ%~Žã¨ª¦©ÌǰÑ_jØ­¬±’6,YL"Ãò›9°Ç¾¿}ɪ ~d2±­Û w%¢wÏŠqêÙº°^éwàxá `S=;²€âP¿¢ï[½¥ë£ke¼•Xÿ©~œâÐg¸F¼î,O a-\Ë)Ë›#·ºegù€ÓënH§_µTy¸GVqÍÏ ;ƒ?ò õXÂ|Éy¼UyœžÑ»÷îµHâhnÄ ¼cF²U"‡À5¥/Ú9{!Oì¦Qº\s !:;ûCt§gÿ@5©±þð[ Ý ƒ´%ç4óP}ӸßÌÜìí|þ8Ó/­‡; 6 C@)cÕ)?'ɹ>öÌÑqAF ÓÁ2Ä3°Ðkå¹VQRhGuX×î#8LÁìe}"Mz´s7†~¦Ù}—c6*8 [Z7°cGÍ9€IÜÑã#Ø2´ÚRô°3=„sù@ÀÚVÔ"¯€ÉUúÔ¤‡@€fÊ/½d-nu§ì k/pæ‚päÀN¸W£¶’›Ž£6mØ`oÇEð>øHº|`dÚn½ V_„²ßàáƒmð;»l˜£ŠkÖÛÛîº+¥ú'€ü$(®B\@0»ŸSå%L4Äþÿ›¿ù§)póÍ7ãCáNg5PG 2\UUå‚Sõ꜠ÖeþÜ÷å“.íKéîðë‘~}‰ ÒHÉ—«/…4i2ʇ2ãæ– )Õó ±Ò>ÅÉXWóêý©gò* È>ÔÅóГúåYÿñTðZ™‹‡U–XÕP‘ç"ÀŠÿùn„™ÏCÇ«¬SÓoXÃièKZ™ûó¶¦g:#ßµgŸú¬ýãÿÕ¾óÈçí»;¿µËïØ ó¯á¯¬ái<á¹…ñ(v+*'ýŒã÷å[ë7¼+Åú—î7À+N_1ýžÓ~4!¯8üEƒqíCNuË•©<£üé†ÐßéŸëÀ»üÁ/XîáG-÷Øÿùÿ, ÿù¿îòžÇG:¤þÎÿpâ½Þò^œFñ¨/|cúMœuŠ’‚ãìÔAøS³¬«n–|¶[r@ú9ÚÕk¤¾lV6¨ªŸd¬@x~“mj®_€iË9Îrð:È qä挛"ü¸ØÃ/&t&ño@Öã{5²ó ‡·è¹Þ`ù é_ÛR.h ÑÈÁÒßÇa”–×"àYd‡åH;ùÓ°è“Ö\ÖˆúÞ<€&¬4™o;v>gñlrõ•W[CY>:ýcÖ(£)°úGf‡mms+„Kø¨²~moß$ÌP}ªmtœˆªé‘b…ŠIY `!ŽW6®\çH©ð­¨¯dñͱ«ÏÛhýGm=œ9’~½<¯ÍpŒ±~c‹M<¶ßúw^ú´ã®]±Æz1W|ím×;X= éõkG‚Þî#Ç0^”Ç‚Ž=‚~¼Fçþâ诇A¿ñ¿áâ3 L_ö1ÏDj‹b÷Ë€TÅ@ îE–ÅeM (ýra ^«û m¾7¿Ôzô¡²ôÒ«”‡ eé6‚ ±vÆc14€@„AÄàO@ÔªËFÞ±vg’t„nh{®®áv±òÙÑ:» SºE˜ÒåH¬¡jÌö÷ÕÙºýEÃå%ƒ¶E±de“•4¡òW {¹€¾ÑÛ?ò ûîãÿÓþï~ɾ³õ¯ì‰í;ÙÁ% 虄[ÒåRÏ(öüT¦w#8‡÷Dò„næSé¼ïÛ]ϵò=Å®˜ÕWh©8;/ ÍR#Á«.^`O‡k˜ìü™öúÉŠ—Ø,ÌÝj9=_´ÜŽ-÷ø·ÄØÍyá,è×=ûß¿nvõµfŸý³Ó¿ô'~ñDØ¿ü;_vߣq§—°OV’ßÇ+d£ÅY~BÂͲòÇÙ¿ßH €D¿l³HåOÞ`ýû–ŸÎû£Í:wnîÖe–]¹²v-õYï6+ @& íø.R¨ì¬ç€§ àl,§^ þÿØý£“_TÛ#=îh’‰„Î?oŠºüD1¬Lä0v2=‹zN΢MLkÒ€(û†a›V`ÅúC#VŽ:Iºÿÿøð¬¥¤ÁÊV¬²vËõ•5ýƒŠ,œò¦„ô«®(µãÃ=ÖÓ3Ž¡*«.©´2]B~5ìÀ¤õ\U\i}Ãv~ [.‚ü ”r>ÛØºÂ:úc‰­ÖÙâ"?ï × ÂUƒOµmÇÿè^'mßÃùºŒ ­^³Ù†÷ocb'ÜÎZÂwúÛ]aU[›þeI™-NÌÙwïùvJ(OˆC€®sûí·[{{{ŠpÏðGò"‚ÁƒpþöoÿÖ¹¾îºëì.¸r.$"Cc”Ê੎ÙÙ$Ïau¾M©ƒ¾CåÝ_„÷©2_ã?yˆS®û|ÈêÏ!Û¨ÿŒŽ¢LÔSªJðˆ;Usf‰ì>âýºº¬1©¾¬hÂ:Ö ¦‚(&Ë_cç£Ë?:À.‡…­ ­–M¸žC¿yvt·=üì×íî×öìûM{èéû b·ñØð©;w737î•K¹ƘÕ½ NèÄß8iàRíûö¿ê<݃r éºtí©ÊT¯wÄ~²ë¢›ŠAœ<™Ý6;oy&ýÒ¤Ÿ˜…³×ó¿ØéßÒÿ´×À²:›>‹ÂGÿ‹Ù»þÐ]ï<ý±/È¿þGY+kÊÌŸV.WD›\gén güønñ”+çò2I¹Tfe•°+t¾¯ù)£@¤¥û/بŒ:„FQï’þGG©|*AéØk†À"}n„sëºÓ?lÿÄa¿kAB.À½›9ŽÊÊðWŽ~ç‚Ê‘rÎa÷?7;IÇ`ÏOÈ”bh/PõÕöÄs{­$>APEÜ3:`E&9×/(Âa¦{—†±ïßPi-í-ø™²‚ \ 9a¾Q8sìÖgQ\¿aAv_+ª"h†…?²Å Ûþ¾ÃÖXUëXÈý Zdàý£V‰mÿ—ºrdQf_ûÚלêÞ8ד˜Vöyߨ ­j^Á‘A®¸{ú'HÜsPݶÚn¾ùÍnÿ¶·½ }ÿŸsª„šzZœ#é-}XŒ#1©ÃÙºêDèâ¯ÿèKvÅú-ÖÔôJ¾¼ô$ב‚þ‚ÍqäfXµëÇ;ÞáÆ.N€äÄ!PzÙÀø$t‡Ÿ%“å$tšxôÙé}Ç?¿eš4w0¾Ì#sµ¬êSAyþœÅ‚ž“CrÄk\Y€Ëj£bG4„úW«8Ò÷{²þGfíòu•ÌŸ9[¿®”£€KŽ%°z9dšŽÛ]Ûƒ~Ëzö3öÐs…ã«û š™ÛÜœÿrŽ´n8ºi÷¼ôœÂ½Duá©F-Rõ¾Ü7ÎþMשÆ?WõëûÖ¯' û~SWÁ„ÄcßÖ—ø¾Óµé>üMÅóÊ4äÎ,^®¿ÐÃiô«þì–Óõe‡ôsº> Ò¿†„¨ÎÎðÙßÿéÆõÀ|û_þ5¿S§„–µ>>“ß%Ì»¹‚–Š·ÃQÀš@àÒ ]‰c¢w4Î._Ìüæá+-?´qšRûƒ…ë4ض%ààf¢E4•:1hÍ8GŒiðç °Ð)a ¿8ø«ú9X÷ssãÞñ /~·y9ùHÜÏ õÉv)ÑEtCsp?šâ™ìfÖÚëZìÅýÇmÝêë<Œ:a™ÛÍÏ3‰f "Ö¯nâ('«o±m[ŸE³¨åÍÖÚÚˆðÇp†GÑ=e'žœÉ±MkWо×VÖ6°‹E¿¤KlÓÈÌÚáá£ÖÀ‚ðIg˜¬Ãh ŽMA ¯kok·ã®çŸ°‘#è;”CôÛ»Þþé¦ÁèØ¨íÛ½‹É½„Kì½ï}¯}ìcƒ¸Ù.¾„ÎÃ6ùÃj)ƒ 2iŸü•_¶Š /!,&b༛íÓ7ÜþÛ?iï|÷»RG®ƒWøçHF ³³Ó¾þõ¯;¹~÷»ßí]ó Ä ¨«ãH%:š—1ágn"$F1À+ÔñÙTiÅú¼õÊèÞ…(ò™¨>Ö…+— ‡‚C¬¡ƒWG”¤ë CçF ªK ®ä§ÿ ×UOðqè=À”®µÃ#‹ÖŽKoLjÍn·»dÛž¹×î}üÿØöCßãXé',x®iê¹q‹º%!瀠ý÷å¯z^ÑÃt÷©ñDP—ŽÕ«*ʦúT™àü5BNqº¯x+_~3¿ùPš‡+ª4\)^Ò¾.³íëœ[¬ϼÇrÿ%Hö~×ï°ó¿”A¼öHÿŸñgîái¼Ò;ï鯒g~ô³ÿŒÙ·þ>Ýr;e¯4$r»˜S n8[E˜~—[`8&åÕõL'¿œþ8SÀ]Ïõ[)œÞETÃ½Š »}™vRÿ¥ÔOÀ¤ v–pž• ÚýKà„‡¦¹D€£¾ðìÿ=çÎÔc׿9Îæ¦9Ï/…{ƒñØ?’•ƒ“¼¼¾)T¤F†­Cù.€¸K1ƒ:‚þ}Suù̳α‹­džcå¨=€À[kÐèpo²?ê{wî¶}Xð»dVK]A3gùKÔ×ØÊºjÛß}ÄZkVÚñÞAÖ'Ñ“ž6qô×5¬bdšp’]Èå8©j¬ öO X5ƒW]u•i7¿ÀÄFÓ@3®{¬Ëª®ß`հοô¥ÿíæ`Y g[Å«8{wÈ\HsûöíQé`Š~·"_RTlÝ£ƒöÖw½Û±áÔðÆÞhyo]a°ôÅ‘#x5‚”dt, ®€þž|òIûêW¿ŠÌFÒ¹–Ú ŽtMɈ8 ˆLÞ]p,7ÿá‰&ç{!Àc È+>^}ƒ_»4OÊ—þ×ÁjÊIJJëºÅ‚wˆ– ©> ]?ãÒ]„rõêÜu^ã7æØ8õ<ª ‡íàÑÃöÂñoÃmú3Û³ç^ëêß qŒàhŠÈñÓ#H—ŒÚ;¤ï õ“ªu NyŸ”>|R»øPž•ÏëW×J)¯r'=ÒÐ‚Ë ñk¨峃úòu¾Æ_9\)g·;|üj¡ß—i—lcgÿ~¬ñ} g;ÿd9ÝŸ§x‚ýeZ¾ªÕ¿ùYßÝiŽú„kkï¬pËEfó|:þ4|ÉÉ¿ñ5zÿð¿¥a‚ħ¤gƒ°þu7lgØõ é³³žÆ¸ÛJ¦†88û‘}Á'Ǻ!8¢•E@íð#â@Ç_y–C&œ‹Ê GË­K©@:ãAº9÷ È¢T‡¯kâœ#rsòx¸é'®Wál>ƒ´á}d Ö$/z–¯<éaÙiw¬N⪇•nžóÑDî’ ]† <ÿõãyo Cæ5%Õ6ŠÁ –º&ë<Žú~BÔ•p .ƒ;û»í‰'·Z.DGíŠz»ôòkÜñÃÔ zùxbCCà}+«ñ¹Ž$Û,»üq„%›°bE™ó@X,U,¸ Mµ/š³Z$䊭¾¼Ê¶¿°*·¿—½A­fˆYœó`k`äû‘¢#ØXu¨v-õìÂÊaŽ3ÏûÇüǹJ¯µµ™†•VPWÂQX¶ ۣ߸ÏÝCøÑî|Ällgöì±Gïûxu§ƒîCGA6 §§[ßÀ¡Ñ£Îóá»Þõ.DZñ%@B‰:Z ŽâÖ€cœ,/ŽM"§ÁÑÐ!.­üB`”ŸðyT¢ªÔŒQB!&©Gp¼¬t‡Q]hâ€ùq׎p(wñre§ŸÉÍB^ñÖ/™^ŽC2ŒÁªçí°¡Áï[×è~\ü†%5ý<ÜU£q²!†ç˜¾ß€X=äï;ŒŸº4 {¤¾{Ox…G,×NãW:dR¥¾¿ÐV¹4ŒËyŸjœz§"6ñª‡è/]’NéJá/]zb*}­ëühO,Ï,I,¶±ý %:þÎr|›³}yØÛœ tŽå®x»ð ¹ß§O‡o¤ž{Ô·éßâ7¬8±ìåK0Ý.G±bÝ;I}vüŽ“2ecƒÂÅl¸$ -S±8’🌿ÍLÂ%hŠºŸæ¨¬Õ>¨/ÖxH½òT‚ÚxZÙ¢…Ù™)„Îðê®ø¯Ã=OM ÷³øaì×=þïBó¨)å¢8#‹}Ø(ÄðÎì&9ZÀ2 LB.ÊÅ#ÄÂüì FR’ìö±ŸÆ.½‹ÝúR)þ£Û7au¿|•ë·êÊRë›:ÎEŽÚÕÍXšû:qœ0i[ŸywÁXcë˜OóÎ¥ï.‰‡ç†ìB û±ŠUUax¿VëŠ*×·ÎúÇ9š¨¡ï14F@pëV7c¯ Ä:÷p½ûøçdh‰sܪª=ÒëìVæ3ŸÁ’ Ú–Ù>lãƒG޽ÎôøÃ:ÄÿÁ~Ð!Ø .¿æGÂŽ vÙgwX6ÂB)ä,C>‹†øÁR >„|_í D%ÃGâ•ÁmÛ¶9!A·Ýv›s5,™:¨(Õ#öñ9Ö›Þ41 äƒî Î4œ«Öø–ÿøNTu®B  Æž‹Þ™ûó.ÀÜ ÔFÚgV?®mÀ¢¡W»þh«X]ŠˆœC¥7ñý&®ˆŽ5ÖDÖb†C¨áFB=$e}Òß¡ƒM9*syŸN_FDA 0㱩4‚Üÿóþ^BÚÇzê(Ý™ïWÄGºŸJÃÄÛºg \œÙ_FÕig²¯Ærê‹«@úa§ÍýN~ï¯rÖºéÔΡګ®K¶s—O³,¾lØpaÉrAŸì½ß3»jcúÍßó2Ø~úÁ½!q|ÅÆ—JdÌâŸÜ¿»žõÚÀ‹ÒÉ`sq™-€YÖæD΀´µ°% ¤_V ÜA´þ„ÛŒø":¦…æìÒÒÔôâ _æ¹Î9`b¤[ÖUNŒyèÞÉsqIÛõKJ^;åYTõ¦'a÷ÏA !ºEÎxòܹPžåpyQ/\£Ý~N¡99ýÉG]ª½<åïyð0½»Ê ÷MOàöÁŸyÜÝSVÄ®¿v}³Õ¯h²£½#6ôó[Ö9$_Íÿ1ßüTž5  xˆÙ_‰ðÛ(×–ƒŸnÎ÷ÇXÉ7­Yávþõ¨( ¿ðìVî?„3€aXÿØr/-·–æ•NZ%¸;>pè(v $Œ˜ïÔùtÞÞÒÒb}h\}õÕ°„q„P˾ÞNÌ79Á»€TD”1Žéñ1|Ç÷ ’Ød^xák6kE á‹# ‚ ¿¿ß¾óïØý÷ßïŽ&¤2xÅW8'E%«¤ì„Ï}xúØ¢Qy’B|Bˆ˜\p\É D2@î^;ýŸnyn¹¶'¿^bqƒ%&>ÒÿHÿ» ý_âN7Ä;<+ÒñÿtvíË úgîH—N@€+”6øøT¿oŸ¯ ¤½žæ/¼ _ûÒÂhšo×¾ãT½dÖ•Âá·D¢Rõ$DRÀsRþˆï9@|<ÚùË)›sD=,ÿd’rÞPv`œ±pƒˆ‚¸ab¤“…¹Fœô£9°Üô`~â üÉs#Äçù1bäÁ 2(¬……IËáìb|€«)OÐ?ú÷9Ž#ÀÁ?œtváx?“‡Àéé>ÌDAžÓãŸC7ô'Û÷[ûê:[Ç¢gïóLÉLЦoUª6k¾n£3ðSˆ\Áôô¼í<¼Û.Zq>ÒÖ¨RíµÆ¶†à`qC³íé9䈋ÿø‰O¼&\€0±„¤dAP^7@¿ò•¯àþ¨»oÉ?´¶¶ð£/,||ñ«3•…|ˆù.År<Þ7 ¬6©oUßnì/ú”]}8#wXL˜Œ?7†Ô8Ä¡<À…òõg¿\[ìÅ/nÂß/ô¿Ò—‡½Oð VŸÉE^wØøîúåîðdƒ[s3„@DˆÁúrá­w">– ¾ÝS%„ëN'Ãî~z¼‡¹ç[éx· >kC~~5H¾ŠóïËù8È;9aAÔÄÇçŸJrqÄÏw‡üõ}ð1f|T±l|Àþ²nîC ’ çQÍÙ‹€l8Ft§°€[ÀÇsA>úþìØs(Æd@ârö„Ý/ªp‰3îB(¾¼¢zvþá•`à }x;Ûµçˆ]¸¡VÀ “ÆÅJ ú•„!úúåU5öì3ÛljÝ7ZÓs6ˆD©vâ“3ó¶û¥—ìè¾'DrÝuW[¢(í”ó.ØJŸÌã"Šv² ¸8Ôò@â²½~7Äå¥vd ÛêðgÐÝÝm7Ü‚ÍÿÊ*›Áwz3œƒ£=Ç­‚û[·nɈ“eض}Ÿ]wÃíìú[ü’CªrÕ+$»k×NNž•V—Ûå7^mu«Ûœô}0$€B¸ ŽJfÁ:͹UV†!z­ƒÎøEÙ:¾G@rR\ÙT뇼rJ/‡üÀ#Xíœgç£#hñ18yÚ 8¤`ÕúŽÝ·œZ=Â*B¬25ЇGY¯M§³ÛE5êM""Vĺ—†Ã¨CöfBú£éø^GžØ××Õ”ù¨CEËÅjýÂLªu Þ£ewKQ½®»Eß …‚ ÍN¼o׊ú0æpuqQt]ýóAqx4¾Ô•„Q¿B¼¥ªC>;5=­(ÞÇi5ˆ€°¿°¤ÿÉéÿå |„'Ór&ü³Â6]¾¼£)ÓÙW”b9uaæ4N½Ñd>iˆ¿‘—;NèxGƤΔ©ªG¶‚ɻķ“ÏÚ&¿:ê•_˜E¼.¢õµ˜”<¶0f‘é_éÿ‹õ¯à´D(TÑVSñQ9ˆe~€‰¡ @\€s"œ‹À´QsíÜ qcû^g÷‹ä ²›_pþÒÑ{sˆ§Ã—)[¦(õìB3b¹Û!‘J«žqjì*K݃ÊCÈ* Ù4Z÷m£^i•†H÷: q€q#¡P$FH &žöµ¾,´0ËåÕ·Ú¿\8zZ¸Ø£Ÿéÿ£åýkþ‡è½ùå:?+ëo…å~&A{ãʈ_®ã¤SQp ˜åÚ-W¶¹Ò—žŽ,r¤ÑŒKë#L1Œš•4#g5ÀNØÁOãÖwN¿tü1‚%Û^»d/Ûî˜äïÅS`1 Ö‚'†ãc STaóÊRS'•×YìRä2çÀϹHèeJ´“—ÂÃgÕÔ+˜Ÿe°›Çú^ÀY´ð0‹}NNÖ=a‘ÙÔÀ@IDAT,Еb a¿Ü$HbºbvÖÚ‘ÇO:Æ(­½±nB¡U¡–§´nU#ìäiÛÛ×ÙßV†nþ0+øëWÙöîç±+ Õ‘$–kìPÇ›>fG;ígn¼Íê›°É>G<:ªÆéÏÐÌ ­®ke÷7Ò§**õ˜i­®*´Ãèý—ú¿ÿ‡÷ÂÆfÄŒ$þ÷õ欬Óî¾ûnw? :@v`dz[»vù܃vפÃ#'pÏ=÷œ-ííÂåñg3àúë¯çÑ%8ÉE%fS°¶¦i•9|®€g^¿çï?þñmßÁñAľ§€èCƒJqâÄB N½hg=3‹šâÌ’ Oð 9š€0b#/bÇ/ÒOx!g÷GLþ  @êÒBПvì"2¦¦x¤ý»xíæ…èõ7ÊZÂikã}sì¶BQ:֬ބtˆÓ©P[—¿õ[¿]f²õ”0êBÌÅöïßïÎÚ¥‡ÿÑÿœ³†yœ#ŠZ|d¯]¿Þ>ò‘@$…¶è¿‘Ž$âÜ.èËrkzøÄbƒ È=äÊbà).B˜;Q¯®Ž7\»ó¡QÉXHðWÒp d‘P±ñ†Ç—Aˆ"l9Ð?ÉYýù›Gb_ý.ByÖ8–…u…¡2Ù²ò!«&Q3W•»Ï%ª‹÷À=˜'ªU¦|€óq€¤"ª õ: v«‡ðçÛúrõ«ëø+*çS6ôb•§¯¤RŸåÙu¾7Ò&äUÒX¥œ£%†? Ò¿¤¡=üvê_…­­Ä?ûïßÿ§BX¾ÿ-ßW‘8åö5§îùúÉÁ´ýR@,ꤡ¡-]uée8?nÁݺ+’íÿ§ÞW\VÃÔPoìúE°Ï³–Hýoj\öÿAö IÓ£Î7/h§*¨~4ô‘p¤|BÓGñ4Y5SÇÆ’J8gÂ9I,Ìω?a™Î+¨æ}Kº3ŸÛ3^²ø^ûôj„Ó¤-2?7bùx}š–\ûÖ•×°ËÆ†4¿õ™ß¶Q&úæÕìÐgý#]ý˜á)sŽô"nQ¥iRYÈy=Z0û»”‹t;^Õ†‘¦ŸÂà@?[I¹;í©<7ÜmŰ³/¸x g½óÖ¶ª‚c‰‡<B‚ÃØÈÇ@…Xâs Õ6!ÂX€!j+_XΊpo|ÉêÍŒ{šv8zÁ†@Gw]|§ýöïý®ýä™§t½ÎÓ›››ñÀÇGЇ…¬ærwßSÜÏ­7ÜlÏ>û¬=ôÐCÎøÎúMçYÅ».¶ñ„(s0e<ãÌ /g¢Wƒ ?ö¯fZ2’ ¦€l3øQ¯Ï+ëƒs× È>>ˆ€øOU‡W¿úzOÚuòÇÒæÀŸÇŸ”.P?P ŸN×f¦ÂõÔæŒCv£ì¼ïЕ걅jÅáºñk &Ê«Úit°i&¿²®(Õ‰¯óe¡‡è" ±‡Rõêát¡PZÆ¡Òép5µðé¨o_•)o³\>^¦kê/ôÅyðüÕxØûþ?Zî±?µœ¶Éa55ýÒïäåoR¢AB‰¸9qaÛ½™mN‘|ç@x“oý€ÙE7èL?óÊ}è3ËÆG¶Y]óF s¬¢f»ù<ÔÀÁê¼ãœDŠYÿ ÐÝÎ? õ<6‡h…±1ÌÉù;õ@­7°pT€\—â0žŒ+žâ¡I-ø\²¨û:Ý÷–ñ þ¹3‰œÜ#ñ·£³9œÜú KoØNù\$ÊK‘‰à$ý°vfcˆç@3vž”`„ô6oÞl¿2m¨ªÃþþ°UÂêßs¸ .z¡-Mµƒ{÷ØÒ6hEXF]}ÖX_Þ}Õm¸Èò@8#XüÃÉd öôxiÿyØø;ŸyÎ *m¡°Î6nÚ>KÚªº•ì4!¸Î$ZR5,Cß¿ƒâò"·U6 ¡0‹š3¬<<bEP «L×VÈr_¾­¬r¼eËõn±×.Z¶p6,òؽª’R¬ ¢n˜äÃ" aA*\ýƋγÉQëïêµ{ X.ˆ@’€ þ^kä@_ þRÏ˜Ê _¥#â_'®ž2ÅÙ}¸vQ‡Jg‡ð…=ã]:˜Ì½W’YœÕÛ)²jwʶñÊxú}ªKƆïnQ?ü¹tÔ•»Åìnß¿3w€]2Ü·ë+uPÏΗ«RéÇ:ˆA,ÿ|»x‹“§Ã „8}õÛ€ÉæÞ ÒÿÐâºÀ9ÄÑžkª¯@ÒŸã8•LTÞÎ(DŠlä³Y©-«„å_舃‚´D±æÎÚ?þîÿ¶[V_ih@hñÕ™©\ÏùÏnM+j‡°ùŸþÑcNò^»ì®®., ¾äžífXÿ«.Úl›Û7:»ýË!xYê[œ¥×š AI01…WôðyNþUm,¨<¢)á½æŽÀ)Àtßí}½ž¡%÷r¢k áy´]#cpQw¡Mì2'$CóP¡|vYFA¨Ô@C:4¦$Þ>¤X¸·èVÂ]OL<î5}ëº3Ð}ƒÐ¥¿T|gŸn‘DQ»ÂIÒ¡zËì9ôb_‡SÛxåý•ЧKpÃænBMïAúr«û‡ ýÛ€ hÉ5øýž”nRê± ÏòñÉ~K>Ô¿ùv³;ßr>ddyÿî“é’šu>=x8]–*^æ´ÅÙòÉŒåsòŽbhí²KáhÁ”@`)\VŒ¹y“Àº‹b>e9w›f†ˆtɵ"±2´& 00ª·„5Ä%ö•2ñùDù©‚@1PÇFô4CNÕÙëWÞßëwÅWáJÉùÙA¥·H‡d¨]ongæ¸wLJZ‹ ‘k; €ù‡ä"Z€g@¿”Ë^@ÂÚ÷<Èa?DOÿüÏÿéø9»áηâÒUvØsì¶ËK ±ìЈÌ@¹=3‡Î÷AëFB_z¥“ ÷‰ý/Ù5.AU:lê¡Û¥³1YäxˆC-O=ý¼ õuÚ8Ê7Ü`˜)–Â*âήk¨«pçQå8 bìµXûƒè82Œ{clp.^îv“ÕÝã@=ßû¬mZ±‚„Ý aÆ vÙ¥ó¥w¸üª•õÖ9Ô… A¾“¼W¡8$‹ >±ÝlfxÄÞö³wÚæM›¢w±=ë/ùËvÑE9'>±ª×$‰­>¨¨k}§J+Ö_î£#£5Žôâp¡MˆC¿ª§#xÝ«ž¯N¤‘‘¯‰À]* ª2cÕ‡1dÖdåPˆ³ªcYÑ©>Ië©a„ëG·“ªwíâ°QZݹ>¢Nü¸s× ½³å­Òa¹²Ø¨tW`ØêÅ÷äËBZ9âtûл¯O_5žR?ø™}«åô}‡;÷ôÿC=×SžÖâíþ妵#áî? ©ôÓO—¤SUž˜*`¯dþx*ë«}r=ÂþÀGSEö–ˆpˆÃ¥k}ªnUv ¶ýé²o>™N‡Te=¼Dë&¶`°•’à8wšõ¶¤œ#add ½ˆ •Ìpô[V-D? 8xÚ­p«8Fƒ@¶…E¸wƃÀ•\tÅô CHÅ‹K‹sɹ©SH0¤@ÏšÄ9IÌÎŒ..Âç× …É­s˜½EâkPòÌ'óÀ2ù+Ÿ IŒv@H÷l´‹%€ý?uôa¦ã‚rµCj’¾×îx°ó0By%XDªž³è úäH/¤ÀJAÒÕ¥ìÂ'm©±qÎçwv®½ ƒù õ vô váÆ«)ÖŽE†:‹éБ¼Ùýýتa;5µ8÷Âåûõº]{ï$Z+„ÿ 3ÈŒÏb©¯ÚŽpì€7A(šF† Ÿ³ª¹Þ~Lc܈c„µ­nbi±þ÷ŸøyÒ8lUåÅ8(ê´¶ÕíÎB _Ì¡ƒ9Ž8ˆê$‰;¶¿`s=cvÓÍ7Ùç?ÿyg/À‰8ÒßóÏ?o×^{­½ï}ïsù×r;—žñ Ä?¼°ö‡2å@F±ûxC…¯Îø U¡XeŠÇšÔ‡&1P—Lê+´ÉRÞÕ &úS™Bj ÙWP>TÆÓ®•ÿ‰š„Ëês-Ò‰ØPàc=¹äòW »¶¡g‘Ð!¤Sº1õŸ¾AÕéO…ú i’ä9áçYèÛ×yx¥— qØXýR%H·º=ÎNÿ1ËéúmKL^ @ýÅàÿ%ËVú›]Ï£;~ôônc¦áÒ+2².ÓÑ…8" züNÅ6jò)Jœ"b‰9!hÜâÓ¼åƒfW^}BµMŒ²ã/G ®î"ÆÞJÊ "À#‡Xã9ºÕú?ÇáÂbÂÕ‹¬ãàg­ ä]À:à¤TvPœãwã?3¢:+€âÔZâÁÝŽ’™‘¾çf3:ɬ=[sË<ò³u¨éqÍNá?am·€ø•‰÷ÂΟ]ÿìôiX:Úýçðœù$8sOâP7[XŒd‰SNÁŽ÷Ûe—_frU+UºÒ²RûÚ7¾nõ Z9šA;`ÇîN«ÎEXöÑÝwÙ™ÚFR¿û\T;Qè^}>¦9W*Aíd öëÛ 4 «+¯°Œüèj©ÿ–Æ*ûÉžçlmÉÁ~ÛxþfŽª¬³gÄV4V£ ؅à: aÿâš"$aç­[i¸-®_Íý¡µÀ× Ã0ï<>¼k«ýì¥7B ç8K€ûöï3k¸Âæöö:ƒHdžzècƒ}kbá'¹/[`¢C8¬`LÇ;-£CÒ&hoowH^È^ÞúÄþ#ΟâpH¦{/»ì2«¬ô܃ô[yuS9¸^ã8&\F8À!yJ§BF&UêO6>Rm¼ï+º®£yˆ ßQ±Jtæ<ðò¿"Þ](KÅñÊ(­(«Ø#ÞT£Œ„@Ý%ýP]Ó0Å !ö¹ô¯¿Œ¯ \‘ôÅà B8'¡7Å!`Ôw(ó×ñ=ÔgÖyˆìßKùR“w«ÛñW–{ø!>nu§9¬þ·ñ.½ÁgÅýñ…t•ž¤žøfkع‡ÚÛÞR>~ÿ‡2óÊé¤N!¼5Å—Üꊬãõ{*#@ ü{BøÜÿ6;À ïþJºê?kö“§|¾„µir »(x&ª¬Ý€-€~Ö{¨’¥wŒ(¿4óróX¯:ž áø½$MHC@wï>¦Õ1 Q²ôÃeé¡ÄRÌýŤ:ò9gÂ9Iðtç¸t‹s4ó´.€ÐsÑ(ÄÌã"/tÁ?Qv‰D‘-ÌŽÛÜÔ ¬ž~(<Ôâ’ˆ€DsòÊìÅÝ{­­­Í¶lÙâ¤ã_À8õ†5°Ï„Y²­;:lU۫⨠££Ã©«©\ˆþÒ Ú86˜±vT %M/£BGp"´ª‹~ìØ‹rKÐÿ¯@ž@»hIž&¬Ÿ1´T6Ò×aÛ¿çE+ä uÝy¶~M+žA0Tqù˜.uÂpÅh´·48{›W®c\k`Yp©S¨Û•õp!öÙ–¶ó¬„- {üð>îˆÉB»ü‚MÖ72hLÍ£GŽ:¤®g³pt7n1tÉëèÇÒ î/·?ÿ‚}úÓŸv¦y¥*øîw¿Û‘|ê×?mWãªxçÎö…/|a“Á¯îT: OJ«•‚Þ¹Kóãvý®4]æ`T•»snuí¢8п¥³žÀ oNì|Äì#ïõy}µÚÄß{ýŸMÃ(õ–;3ó!÷Ç_)p¨p'aÑGv„×ö‰_ö™­G…§m¹òDÀÛ!Bî†_w¢Pêa§'F­Lëï"Bß:9”Mq?uÖŸÇ1®8@IÜÇÈî ¾VtÇKÎ Žá8pFP·v¶$ȱ+Ü|à)äæ©/Ný¦Š1œÔŽêܱ¨{xuWíÔSyÍ N¤¢E‚Ì^™—0 ¢-d',ÒTöþñ¨jÈ-;ýª“À åàNÁÁY(ÕNç¦ök_ûšÝrË-Ö—µœV3iü´–Íý²MWXqÍJw¾.{õ:£Þµï0ÎP;A ußs{œ÷¾"ó>lûW–—a÷?;í|ÎÛõ˜—œIá¡ +G˜pUuºþÓ6ÀŽçv—c\p‘õ"|¸UÀiޤ‚&3½E0uÕeÖ?:h›ÛÜK«‚« s|íø[WÔÚym9T.„´ö¾´5À­î(ë ›Ûm¨µ—¿úò_"¤³1ƽ ¹Û<Û´®ÙöâÏ@Ol›¹òø õ'ÂèýïŸ]T´Ê®o»j ƒƒî9èúé–€tÉO“’JM*D¯8¼êT¹ R JÉ ÖqÒP.Êÿ }gƒlJ¿@|Úg\ZE®¯“tè›ø_¤ÀR‰" .Þ@é®–Q¤q ‘;H@}2Ý&ÜŽ«÷`6–N·Q»ô¸ÒÄPN ÿ¹ã’¨§Ð"<±ØÕCì8´PqúšáÇkKÍìôß ÒÇü®œíôü #¼ «¿Ëžì ÜtKºF_–Œä)¬ºÐìó_ó韷Ï!õ¿.¤Òñ™ýéo¤óë¶ø´Þ^°xãM¾lh0 §TOwf>äW¶wüG³/|1”¤ãŸ½ÞìwÿS&ûðÑÂã„üBYxE l¦×ÆÙpÉ¥o^^½•T41…§±õNF¥ÄÀ&p‚¼áV:bðë_ ªàŽrp› •&oàPû$”# ¥Ý±Øq±…Kågw8W ÜBÚÏ8 2ÏãÌG΀æç†!þ ° UkPE {¿`ô¢æA†ŠÅ-˜Æ€ÏêxÒ¯wìý7\q;cèF Ií­»oøÎÎ:òÔql<µm?úøU¼q úÌOXvü+¸V'.|e‹`ëÁ¸ Fîy„ ka›ïëìbGÍÙª‹…ùR­+@ÍoÀžÄåÀ6®´Ë¶\Œ°b¡ÁaÐ8*)¥hä{°¿ÓVÔ6ºVŒ¾kq1çWX(lkDmq²ÏVV5@,@à&1O××qÔùhª«´YÔ  ”eyPjv²X¶þfÎòpRTe;ŽíAS¢ƒ9]RRâl lݺÕî»ï>Bµ°pU³½ýê»PÔ3N8§DMM|d¯Q|F*„ï,Īv»•g|¨`(Wì(ù¨Q*D !ïØÜ<‡ô"¬öý™¼†à>úДj:‹c­tM:®âtM,¥NpJ Œki¼n¸QEñ=¸#Ü3K7S™»’kЮ†¡RÜ=ºút™j¢ËJ—§{p½Æ Ò0>^Ò¡×t϶ØyØû†åÆ o÷¯sµü·p†O ¥%³ŒJcŸðž÷ù4®G^6üçyô7E4˜ÞÚ;ÿC(õ1K“ X7wá Þ‚Ï% qŸÙ>ÿÉ—àO…Út¼vS:RºÖŠ•>79~ušƒR­{œß/ÈÛkÁ4n~Á„Í@NDp‚W÷Žfó®Xb38='X 8óÀÚÔ0'uàÖ—h>Ƨ©¿tƯ¾¥Å…yQçT8‡ €Å [z?3˜q2ÎÅÁ/ƒ?ïÐG’2,# Ѧuó «x¿W ÙúáЙÀÕ™÷<`ÏmÛfÛ°ÕÊšªœpàÀ°Žv`ùCaÊ^ý7¿ùM¿çàq«Ýr]pé56ÛP`«ÛÛ¬…Ýû úrÑ>bÖ7µ¡E€ `„ «ËªmÏn«,+ÁçPŽ€ ­ÂjÔûF`ÛrÿâKÛíâó¶Ø–‹/Åja±õO 9aĹ…Eëžè…ÂÍ…3á†Ëâ œÌ+á*4”"åŠ>¬BM%g`PºX|9„†&ö§ÿë§­­­ ‚WT0l.ˆ€1H4ìŽ/J ¢LîyešWvù·a7Àêh½±ÊÞÿ÷;ö¿\ú^|16Ò5ó_ƒ°,†¾£ï0†yôJ|pC £|( íô«>”‡8ÔgÅ‘†²poÄjG¨ábw/øˆh]œ‡±d4TX¼"ž=Qæ@£:¥£¦n¸«ÆÕò“%‡¨U§²º÷-£ß¨½« »þx ¥õ§º*éãFájÓeQ6ã i8yØË™ nu¿yØ[ý«ŒÓO'óöÛ_¹x#¾Í^QèÚŸÙ¬:Ú‰«ôÝ! ¾èS;z}üý/›µžgö©ÿÏ5¦È÷4»ÎÄŸü‘Ù5ç›]Å~âŽCkñîÌü ¹Å2¾œÿTPÅ“â{_Ä §ÌÁ» ‡‡U@í'äÀýqÈE‘ «xqZÅ9–ð`Ye3 D°>ºM…ï&Qî„È} ‹IxçV8w €ä|ÆÃÖ ;<ÁKîÐbÀP!gâ3SýÎS ¬A-qT N€ŒÉaŽlHW×4ÀRªµ¯~õ«ÎšÞ>hŸúvÀKíÊ+9˜‚r\8vþÍNMoß¾}ŽЇÕ=9“êïµoEêxE«ã…JGyL¼üR8õ«œ:_÷p/„@¹u¡>¸yýJ·›‡»Â.² LˆÀÒ¨S¸µLöL²Ó.A“±s/aç>¢NhÌÜlx Va¯@&Œûq'ײ¢†I+ÇEžŒ¯ÁQ}=„ÅÔ8V G!0$Òoomsj~’˜Ù{?¬‚ KÔVc!Õàz,68Nˆˆ!q?·€g¨°´{b£ÐÞóž÷8BIUÑõÀ«ùý¤…ÎNÒ±>n÷gÕ»¯1«Lp!„v!‹Ã5C¬*§’¨8…ìTšî0RqFN€jèCˆO»Úx;U†|ô]¸†îQy ŒGæƒÊøS>T‡„ñ¥~¨á®´óQmøó]ù¼OÇÛª$D¾<» âeº¦‡L—« A]¹ÕûØû÷ÂÞÿ;Kô}‚¾W«ƒõAOHçêç_›ù(Äç“ÿÛ)³üts51Ä}ºm–ƒ‹»9/ëD—(òµ— ì3{¢àÕìÞï™Ýu«Ùº|³ÿþëÈ&½äï¶ýl ¼ ‚`sªYFâÙ§}ö÷?‹3–ßb,%‘gZL‚Ü÷/²å(¤Î®¾ ÃnžÈÅÆcT±ç`‹ÚÃæX Ê…žÉ“!*¶Œb€!˜Û~ôS§$(\„þöÄÆ8X‚uR[^kÛ¶m3éCäʧ‚:6Ïc2lÛžV«*/¶Cˆükòk!ÊèWø~çÞ'-ØßÕ† €Àkõذ¬Ñâ˜6,YäÐþVÖ5Y;’‰øû¦ÇÂ'`D¤¨©ZH€ƒ£VœÔá`¯ÕTY^ÿº÷t8KiÊVŸúbFGÂÆðHXV‚~À´ƒ(–›^=·lÿÝž?ÌÑô/šÑUè°Lv ä­O§œrŠ}éK_BÒP嘧™Ä3úmŽ6>¥- GÉæ©©”úSZjlrBði3ÓƒÜY¿³ŸÃ7®¢¾/'ɵOzô~fµIPie£íûrþ:*è.à3ÂcØ÷š(î_—g]-u â©&f^Ñå»l]_V9éw”˜¯… |P;gËmj=nußi±=?·ø¾oBôßÀ}†‚üNó%y†‚LëþõÝf7"h;Öðoï jzÅ:ý?Ò.ÿ“£oñ—·¥ê ò” ê“Ñ£Žμïú›Ì¼TàÏ3»…Û¾ÆVí^eÛ–  ‚H =Ôû¦³vñÃ"E6ý`@h”/jä8eE%‹ÙBÅ"ª³Íék‘X°ªŸÂÌ­/ü¯0ahÍ­ò‘PN Òi!58"ÉJôýÛI+øR 7;"ÅžãÑË Éˆ ~ôýŠˆ±“)Ü\‘GxrXO¢™[JT€8hÄ'ÈŸbo]°À¯zÕ«ìöÛowÄ^mJ)PÄMÎun¸ákkks{ÞΙŽÄßšpÿû»·Y,¿Ò&ÙdŠXfYˆôÅ•<º¹å:K:öÙ"Ч¦`òX造³žÆ¦ÿ€áZ8//ÇÚÁéW‡ÞÖÜŠ‹àÛuh?Ë0;̵ƒÚ­¢=(ëâ‹Ï·¾É!çº7'S&Š(΀€5:4aëW-±Í-ø¨¨3|º­iÒW–n4fB<ÐÝa€556Á1Ã!Ç ­©@f¢®¶¯vCV Y¼x±wÞy@dæÛ%ü ö3PfdÙðâÕg8EɇzÈ~þóŸ[]]C.€< c`6åáŽ2Hz3oÐ¸Ó Ó_4øtŸ¦s¢qŸ9zÆOI©8“ÁaëÍÈœy?‘ö]Tù¾J4¢ÒT0­p˜©´ ¸{¤¨Ž>ÕÅ :!ÿŒ|_WÇà*©Š¥ÎTÂo(® \ÕÒßÌàkebgZ¼ï}óÜÑ¿·ºF-D²ÏÁðò+ƒ,£bëã¹M™Öo`8¹ o+ò¤¿÷8H;’_ÔløI~óËTŠªT'¤³Â\_vV!N?#H}ó{gç^ú¢”bŸDû¾gÌ.9wÊF„ª M§Gfï´Ë<€ÂóºXŠ8«8ˆ{wûVÎ3±8€²·¶F9"å¤×Mª¼Á€À;Å?¾ö 9Vgü13ó7Íqònµ 0gPz$/1=2=9ÂVX° ïz‚êÝ5ƒ+žžù,àä³WŽAœsõ›ÁÊ_N"d&CëSÚöù`·Éö~‘¡AË-·8||ï†VD@ñ³Ï>q>Žt HÂÁ?ÿüó‘ÓÞ»ˆÞç¿ñcûè§¾äÊØ»$ª€]Ý×ÖmV/µmOl·ÒŒJg³_šÁµPFG¤Þ1$f1½€\ë§ßH¥kƒe µ6À¾ü u+Ù‹wDwzdÒ~{ï½¶´‹ƒÚ\[wàA…yN™°U}gLj­j\ŒEÀµâdbP.,/²–ƒÀ[Â(ChË`vvì3`±]ú²KÝõn¼ôDVÚ]¶¨ºÄtÛâÊZ·%!".<€"žç•¯¸Ìb+êlÿ¡6;}ù:¼fX(L¾ãï°—¼ä<û«7ý•c¤K!½1Çļv ڸơº@dÝïgÿ'UbÓ©¸×Ðwˆé©äd !y2¸¾ÅÙ?þu2É$)¹ø/X0½(¼÷ƒÁ1úûä=²(Ц]üàSAùë:O½X/D½~/ý&±F,jÄ"aþhôëe1¿©g«£»?ÎݡȰôÄ2Øê•i¸à×mFÀ±Y*í×$Ï ‘l”–uqnfa…ËðšÂÝS$$@€’öff†L>XùdV@ 1ßó#Ý)A‚•ï4"Ÿ ¤ìeç×¹Õÿ¡C‡!÷Mjú¦7½ÉÙÛËE­4ßåJ÷œsα׽îuV^^ƒaöäS[œØÿ‹_ $ª/¼~!ûH±LÍ!Ð K¸1&#ˆýë—£õWʹLþJ Ø`/bÙâjö÷»meU£åbú'æ`IEó¾ ðÁǵsÎ8ÅÅZÚ¯²vˆvK'Ú0(@êë¶UldÑ¡«ÐìI–gž²úÚ*ÛÜºË Ð‡¯€l”G{ñx„æ`ÌjÊK­¹s?÷!¿MP ?Å09ߺö³–n{`Ï&+ÃßA@AR’mOm¶ƒ;ØÕ¼ÚÞúÖ·:A1JÒ¨ª¬tJ˜®ð1ü¤Vâ3*G^ZŽŸuuÔŸÊÍW6­"Åü,—Vž“äy2’VS©î²¾~Znx2wÕHÉd+3Òt:W^˜ì³¸OÈ]‘ëE¢.Ëÿ¸{¦m=·Ê¸r.Q?©Zž-ðõ”—Êõ©ý‰—°ºÿDÿßÿÜêþ1™ôñ@?æ¼W³¿~oÐÄWþû˜›:lŽë°ÙÉÌKõÂfN2´µÑk¿‘LJ‹ÔŸœ.ç¡矆âÞ×ÍÖkö™ëIÉ׿›ªrÁ驸3Óz]a^óÄx/s‘>–Þ:x{Õü®cŸ?îÌþHsÙ¬îÅ (?®¬sõm«ÖêÙl ¨x 4¨6]1¥6°Xp ]"}Ø Ï‘Ì…ÌŒ¢uy0ùõ­Pèrߤ¸8ˆØÔØAVÞÃp‰#tÒàöô­§š"m èÞ8éòØ?0äÌÞ’íÙ»w¯Ýu×]Îäï¾ûîs[}‹ ø‹×¿Á.ºè"ýjÍ_ì¶’ã!råXn{§²zí´µ«pûË6DÈSÏm{Z¬¡¾YcÐÁ¹ð1|”[¢øÚŠÛypŸ!ÚÊÆ”0S@A ïÃ# 0Üøet!€IáIëOÃóU>ø±íÍVŽI¡ó1ìÄðy#Ãl$ÔÑ{È*Á)¸ùf|Ÿ˜Ñ°þpQ“&X„†Å•¶£¥™ëÁMó.¥ð_ÿõ_önÌclܳÓ6îyÊ*ÙRð@ À7œaŸøOØœ1b´P3p,aˆâä…Ž¤™Õߤ¯ëFµ?™}L®úÕ"Ä<ÕŒbÑF¼i ¿ÍhÞŒö£YŠûF“éÉ骄 a»á­øf”š¼eŠ»‰ŒLÕt>BŽg‚ºÉ ×pðÜAãAþLbïË+WDÿ<öñ?Ñÿ+ýϰ¿)éᆭkqaþœŠœöî…W/ýeR+b†« þÍM›¯ù‡ùKïÝ8Þ¹—ñæø Á7 ê=ê%|ìoÓÛ ô¶j].YiU«}ÚÜ‚$ý–»Í^óZ³ï}ÛìÂPü¯’çéÓÏ{tžŒãNFŠU¼Plý¢ÍL‡‡°cö'ggÚçEø‹Ø.™1ZÝ;ꇜI¯tÄ\;$'$!v:$iéãÍ Ñô4dª‹"jdA½©…ؘnÓÄí&.}O”7ä0η™Âø=ƒˆVýZÒødµ :D’•/ûêcƒø¾˜fâQj°5í]Èîg?û™ýKÛ]¢my¹Û½æ[ßte/¹ä'P9ãÈ~þ=ïyÒ‚aVæxd["ŸUtg_-‰/smÝ¿‰½úÅ5ί¶¢Ò†±ò. €FÇ|jÿ6ž!žAž!N@)fè3àép/ÌA– ö=h••ìÕ¯·î)Da(Íá{§@c–Ã3æ%<„•ÂApF†ñhˆnBïX?{ü5n;A–Vz’M³7;•m+k훇tB xzö––ý˜ü½Âþü5g[×u]¶˜âu‹VXQ^cDLd™pé9Øç¯¾Êö÷k[ÃìÔSOu>Î>ë Ž°[n»ÝvïÞíP ]#ø &«à÷ŠY ä£i’²©*š-¢•åºTJ*6ïÍø"¾Q>«B4# ª>Áse»skÖëœkÕîk…Y®¼+è3\J²2g>ž*j'¸á˜­&ï÷]@Ï9ÔYÈÓGøJæ8,Z ï ¨¨-q#hE ‚]lvg³ÙÛ` þõ߃ô_mÁ»èš ~/Ä=gûí IC¹ ƒß÷}(¹æ«³’Ž;!è¥nu~öùÝ6€¶3…àÆV€ƒòíÕò8/C Cƒy…•(W# ÑàABªúè7M¡ˆ4¤&%JL å¤B˜®¦ Ešüæ+™ªó‹9è9vOG|;€= óÞõ=ùÈrÿ9n{|L%8*äÎ'að8>(Pœ!¶û ùÍÊÊÊì²Ë.sÈxg…ŸÁ ^9?üáí'?ù‰íÛ·ÏùX¾|¹#ŠRŠÓÊW ‰Ã ñ-Å>ßFq T\fùˆ¤´:ß³¿ÃNZUo›¶xõÕöÚ×ý™}ú?ßgï|ç;mýIë «x$?¼¿iqKˆC}r¥EÓ]â?Ñ2Ø®’‰f¨^З|ós´4wÒ\M¹’þçk‘ô·àVýaq1Ân"¢-S\>¸4N6A©>Å—êÏ¥¼ðÉ`DŒ½ÌyØ‹7ÿÒâ-}^ºÕ 6µ‚w±öDÿNŽüøoŸM•õ"k™¼íƒè+ˆø+\ÀêùHÂ%W¥Þôú¹Kë Í.ÿû õh÷Þÿ寂zKÌ~| VQ+ƒsÜŸØ{þÕì$Ö+¯}±Ùoæ þs݇Òü;½ãÆùJyú]wÌU–+Ä!ÜáÊ?C‹;·’—˜¹Cè? €s±]±â€øk @/`Ø,rŒ&I°¶‹î€Vÿú7=oHå)–&ž·Îs/cA3 ý5¯4˜éô+”?‰~ðÍì°ÿ%úÑé@ëF Ç5p|¬® v[ô•4ùç s_pÁÎtî^ñ‚‰6æ4ße&'åA!¾ä%/q.t¿óï8B§ÉyÇîV[¹n©}V†R\U^©“È`ýš¥¶q'º½hú³JÃ0‰R¢@{¡;PxE1ˆòú×5Ò瀇Ú(ÈBej»B9œ²›·ÚØ> cö"t²ÑX¶h XýŽLÂàîWŒê‡lu娫^ýjçó`ÇÆ1”%ö²\k=‰Ex",Ë/u¯¢ 'EÅ;n¿ÓbgVØ8¶Ã£ÀÃ1+ÄѹX‰Ò¢-β®8†j<‰( ±=wPªÒ.îëÊ+®t”¤¨Ýµð4?0nG‚.À3SÃÇUÙ7M›¯Q>ZŠ@úº©ŠZÏ jޥΤ¸3 ê\ýýÌ•Ø4*θ|Û¦éZJ³ÂÖ}#©›ñ)AAXæD5Dÿ²ÑoýzU’÷ú)ý°7º 3ᓠݣÿðϳ«üæg³ÓÎ|áì´¹R>öé õöëçÊ?í3ÿÊ;ç©ø|1}û¥0<ó޹KœYgöíO™õ¤6[ç.Hêûß•žå@õH@õ¸Ã7¿’Þ„ëÓÒÚÇ ÐõpVÿS,”‚½}yJeõ¯Áàìú•®¥ óŸó @?Ö¦`Tf2èVe-à¤Oüõ‚’„¸ ©åbš7¦§÷ûÜ…t\Ð À>fî׊°OóÑ…ö'3?iþOc'Ï,ÂÒÂj”<fcƒ/lhé ÄÅ9Θ&ýGÔ¾ö~ð§UÔ¾¿ìàåXGº²‹_¿~½Ó„—€´áoú"õÚ&ÛöM>ûO}ÃN9®­³ÂìdN.ÖžŒeÀ¸MÀ­NŽNÚ–Ý-8þ)æž1íkj È`¡ŽØö=íê\öõ¹qó`0[ MÚ/ïºÛšpAœ__b§âÕpphÔQ6”Kä`2ûÙB(*Ôsš=±q÷‘gËWž÷˶ˆ”ePš´8’¤r±ý„š”Áøöïv A[ ­ÝV‘H¤`(_6Õoë—®r[0ò§ ý^;[÷ÒÊãv×M·Úåç^j§ž†ÎÒ9ÒŸ˜ù˜‹)¤$ùmÝR×ÝÒü?~LêèãÑÒs¥Eóç~t/3‹úñ¬‰gVârº?ÿ7;;H™Ùè¬rºŠþž¶à¬šJà¶Ý-ø¸+DšZTˆƒ+¸ ƒäålÇFÿ„½|ìó›oµxëû!ú/*ÿü®:7õsퟧr»éÇ©üžöÙ¢r­°$”C,­è®Áé%‘{M+0ãä¡öü5_K߯W1ôìŠ1»w/"ÿî-3*‡§G²]ákV2ŸEÙgÇÖ££-™Ýy¯AN7ô;Xˆä‚¡"Û·/%>1Ò Ð¶¯³Pž$ò &nÕtwáèpáÈœ@ÇðOiÉ@Áð¡TEãŸùzo2{E4Àª¹—ϧ¿†>«bYdàæ7‘ 'b²C)PñIŠgÀHóS/ƒýtu æül"fƒ…/ñÿ…^èÊÈ:@@9r»«½tAæ =Oz‚–œ¶Z»á(O5¼àdÛа–Ù`Ò1#ƒC¶go»-o(³ª5ë÷ÉÆ/&‚p°ûÛºlù’*ëF*P¹¤ÑÖ6¬Ä–ŸmÊìØÛê¤ÒwYYµ°Ÿ\·E1¿ïѧl´¿ÍÚ´3¹×2Ìùd¡0 #Tš Û ù(â%ÃÿÍCö‹_ ­]÷Bëîëçºr¢Pï11™`›€ëjPH”yâ–mHOÚ[mfé@÷A[QÓèò¥1ˆ®CçÎÍV̶@¬¹,˯$3# •>ÈΙP„ޏtUUðÇà,ø Ó‚1æàÙy /,hÀ†Nx¯q}7?1NO B@²0ï;ˆ¦?´]Ü¢È[ Â"ÆÀÀ×W§ç(é ÄÑ.À|AjÍš5ŽXùÕê#8 Ú² ¾ðg·yófÛ¿¿Û Þ@5»6÷Ø ¿ú_Äò ÖÍþ|æqtÄŸüò!;õå§‚ÒÑCÿJX}Y ˆÖŽõšeu&T¿Xe½ñåÿ¿û\'ìît"÷­¸!.ª÷t¿.ž7æ<®¨¯²ûïyÝ„-]¶ÊV3$¦|ðÞX¨³·`¢*¶EÔƒÂpt @˜Y!? é UáÕPDXŠ”ãc´#[]²"‘Fú1kl@!½»,ÛŒ—D!ÊÄÑm»戛Û†)!OÈ#Ô¯j´uHIäK@¦ˆb’šššf1’ 8IÁ‘öN]6ui G„‚z¢T–Ð_ïIëèãÊÄçºöÌ4WðMgsüúо ?ŠºfHòD_©Žðûö9÷cA÷îkG1Áìé¼¢=+ýŸ"æ—[ÝÕAã¿§¿ôºô§ÛSøñ€ÔØc&Ó?®çß¿op®•½Ï›yܽ+Hy÷?±í·ÓìÁÛf–H? Tø‹W#Ò¿2ˆëw9ÓÌG¾i¶‡›‘‚àYç˜}îÓùì/^föƒ/ 48“ÀR¯µ%ÕÆÓžúE³µ'¤—’Rá3f¾GËÛçæùÔ8£ãKáцpX%e³¨r’ô¬ =Yèã2¡1Hä 8AÎa¦GHc9 ‚è]+-zžwæT–@$±ð‘N±ÏÉ'ìíèå+bãLÓšãqÌ“ø`ZågåV õ+`ÄàZ‘Æã Ô! RÞúYH´²·(":_èè‘õ­·ÞÊ¥‚rõõõ&·À"T²ðéßÿþ÷¹`ss³ÉBàÖ;~kÿý¯Xy]£ó˜ Fdónäƒ9+mÏîfx'Ù»Ï9Ép{÷',¯·{¶>fV°_1ûò/¾ç¬‡ÆpæSeïyûý«D‚­ ÿ.ˆ«óÙºw»uµ´};·Ù’ÅÕV¸¤ i@™íÆŒ°¼ÿPå±ðãý¸ n¹mÿaG¼÷!y()ʳŽþœ±¯O£¢öî^ÊÀ@1NV°ßß=ÔkËkq¤š"+þò’𠫝Zb•(†!eÁ÷³iK³¹n…m¹é—–xŠí‡Ç™°ßþñÿÑþùŸÿÙ>ðØ%/})3‚Þ£@„Ä@­?aEØÚ³xðÔRƒ|Ö@×ê?•wà øÂaŸ™«ëDÓ|5ÿiçi'”PEÿÉóÑ0ËuCÅ•îó£+—]$u|ã‹lüÐßÙhó¿Ùtû[¹Â**þ!è ¼ó}Á{Í»Ãù¨‚ŸDo¸>¨öaVêQt=Úó ÂG|{×]ÔŠÞy+ç¯ÍÇ…7(8Þ}#káA¿ãÄýW™ÝüÓÀÑÎJÆò'ÿ·lOu°‰´ƒ´þ4üð˘ "ð{¦Ãé0(§5ÎÑjV[½ˆù?gÀ[@ôŸHô!Õe @ôÇ…v¤oª?v¶†§!€9Xó~6RT)£BÚ*ƒ˜üà Oý!LÖ‚‰Ó(ç ¾¯-À[w·< šŸz úÓS`° •½DBÓ(ƒ@çË]ºÀ‚bldâmOemO$‡‘Hëÿ+_ùJR •<æi¥ü×ý×Îin¢¥¥Å #ûîÝ»!ºùΙOìÄrk¨®wVªsÁÅlw6Kk…?<ðI…ûj>pXÞR{`÷ÃjÎlY‰5w·²{0a¥#Vlß+Ê*¶ôâtT9êÆÓÆàÀˆíímqhƒ-mûíPÇnÌÇmñÊÆC!¶þµ¶£l È”Qæ‘“ãì–Øžìç[30ÄÂp.…sQõï…è+¬iªl¨Ç–UÕ#Jã”°BÌ Kò‰©ÜTWÏX *089öäÖ½VùÂsí¡OXbû^Û¿g¯½%Dm›ˆÈËzâÔÓOƒy©tÊšºŽ¤Ê{ã^¯ÓTðÄ:•rü1žÓ â$ݤ'¤é9nVqï@ïÁÍ2nI/•ÃÎ~äîw¯€r”T@¢ ÙÖ‹ë›"®IÛ×·Œ|O57+ˆ˜wuÑËà  §žzÊíyßÿýHYrtùå—»í¡áIrp‰'@à;l0gÂåKÁí„×Ûn{ÈšÎ?ÝVÖ,ZS ‰Ó¶}o6ûy6PQ¢õQÛÚ¶É^úÊ—¹çkë ¼úuc®/]äÌûÆa ²±Ëš¶'¶ÀÕÁ4úÖ ‘—Öû ºlÑÒ•X œЂô Ø>÷_ÿiÝ]N †»á!L×4-²í85*duŸÉ»⟔õn}¶æc/õµEÅÖÊ$æ3ã(8k¶,¤<(ÆHïrš< í±­ T¬ rX½l?´³å ÿW¿úU{ßûÞgßøÆ7ì¥ç®³O\ýv÷Ί‹‹mݺuöÅ/~Ñ~~óÿ†o;<ŽÀ¦—<ª3ݱî[Ï™¢=#𔦍Œ@z îl¾f’E}K¾úsðiŠGÓÓ/ç^M¢ÒJË^oS=ï°âÜÏ[yÅ?Ø’U¯´j¼OfÜ3xpÒú[4dþžÉ7𺫂Ö"‹îãjþäKƒê8÷<âðŸßO]²Ù'>œß}'Έ¾Šžð1|ö-á$ÕòácLu³Â/çaŠf<ÚPF¥í\9 sšßa@”Í¿C —„ÀiõkÞ×M27! pŒ( t0»<¼+›vaÄüøãè£TGïL7ãùG¢ 'ÌñÉÎÍs§Ðž‰Çq¹{šÈ¿#TÂæ6&±?Dlb¨Õ² iC\å Á‡×·”c­ü²ä$ˆ„æmò í8Ä9^”£Aç"üRx“ØßçËG€€pvìØa7ntIê+NV a|°… G«ðâÑ,;¡¤Î"š1‚ Úýc‰Ýû:·Ú'¾x«UVÕ8Iƒ<êÆ\¾VSl“m8fÅ^Œd!ÓºúP,¬ÆŒ¯m~,°*(Íh¨kÈ;°Ñ^ó®«l˦ [›Wr ³ ô ”4Ð?bË–VÛm›í+70x0 ,*ÒlLmw›ÛxâÁ§ àP)3n™0Cã60¾²'N´¡¶ËWÏŠÜJWǬyã^{uSzêŸüyp.¼þŸ=f?Êð¹±ÿ±†žO_S"zÐÃ]ø&ŒÆ›Þœ^çËÿ•~þŒÅ;iJ"{Þ’¼GÄTU.~Eèõž”gXg"èÜk»WîàU‡úÒ€þ›¶¨“Ž6 †ÜÇp-*1 “è;9ŒÔý¿.ø™`jrr£&âÔ§@F¢}&¹É±^pkªàÐd¦A:«e‰ß§‘Ä2Xc7…{`ÍÁYyµ–]pä›Zé/Y²ÄíWŸ„ÙV® ÒŒÿÚ×¾æÀÞð†7˜t¤ø‰O~Òm ÈJ ¡¡ÁÁ ïÚµËnúÙOlvaH¦°„U—½?êLàpÔg7Þt£cVÆ è ÷’ZFœü a32†Ëß¼Br† |òÚËG<_ˆT ²ñ_8›•üCúÓqÀ:»pptŽ­\¹¦7ÇèBðb?õÕl´:‚î:U(ŒñΊr‘ídàܨÁöuµò”XRÐp&ûgùˆÿû17”ráÙËOÆd1èR¹yø|ÇâõZñ”™É=nh8ÁYLý+p¥ù ò2l¸{ÊzwNY!&œ…H‰zöô •e£Ý“Vj½õµÉ´î¯ýÜ=>S¢K?þô†ßݳâèÓêWÏ}½†uééŒ;ý€Th+<ÿç¾b@Ž›}àÝéåÎ9/8){¦ÂÌžøtíª|ª· ô7OWã8òA' ݈ì]ÿg±1ÜÏ ð'¶G¢­ø‘n&‰;Ò_¡þ9Q¿s$+¦€ÙÍYPLÛ¾®˜=Œÿ#šÈÖój b^®UÑ‚ ¾ÿ/È›×MõîAéoÄ} ÿRA»_zòó,Â/‡?S£ý|¯IVÿ9ÐÖ%lM¯qç Àƒbè”5YUãy¤7ø–æ=j¥¿sçN·o- ­h}hûÍo~³ÝyçÎ,ЧŸ|2›s„|Кššs? aÎŽXZõÒÞÕª~ÃÚFë{â~{„m‚·½ím¶aÃÒÇl7þ+–ÕÛþîVtˆîG†Ø§Ÿ²,ì\yr'Úôàp/¬ø&°¬@>'".¿¹¹À ÷´ÙàŽÝV–Gç‡éXÔ°ŠUú8R‡>4úÊE±º¨Ó=<r¯‚#îa¥>S2GÓkkGŠ p¢@y6îü ì9¸ßŠÕ̦XaxÉ-¸ÖÞGù{bÿVkDRˆ‚zGÚn)À |rB¼i««¯³í<îü*\öŠKCÂæš ~Ü ã~"‰3¢O“V:,¬þ#½H•Ót‚ŸV““°¼ÚHRç™e”¦EšO–riú‰f×*†é3²}}×´~”¶üòl+_™ƒ¨'.¥–_‰ŠÇ†¬-°Î­0Ïñÿ‡Yåîáyµ">Þà?ÍSOoKG^ÿíˆæ·¦Êëü}ÜÞ‚A{çgÍvòœ7þÂL^ý¾ó-³—žƒ‰a9J~KÕWì]ïO??ž³7_aö’SŽ­ÿÇVûÈj©«'!E°Ù~OL³Ê×J&€Âˆ#UT–VGbd pBq·ÔbÒMPŽyÓ™8É\\A'%:W]eÌÂtæ±½sä.ˆ¤ÏLDq£=˜õqƒ ±¿‚ƒFc>=€ I1D˜ÑÓ½, y³Y); h9*(_ p¾WÏ`ŃfÓ~ü#x`yÔ~¾"nB”¢  „D读î:“@f…oyË[¶€, ¾þÉ/ÚOŸ¸ƒûÂG5D±b¼|I ÷PÍ–@ Ì'‘½V©<¹ ,€:kºô+Ï®Æãà¸õ 8mý§pË+B¶÷Ê€tøî1&–8.€sœeÂ4Á™¬ 펭-ÿÕ> ¨Îц³^fÖtnv™:ôÇÐH†W\f&xá{ï6ûËךÉDñÝWàs_@¾’Ÿ…ȯ¿“ëmóa÷<ÚjÇP^WÒŸ¼œDGæ -òœD=‚>Árø¬î©Ãö€3ð¶p â´`ˆ5e¢yŠs)…©Iï“&8_H¿ žàeLNŒí¾†¾˜¾/^›ßhƒfåT#ö D?˜½ed±Ò†ðdA§ðÈzr´›¾ƒ¹ º ¤9…õVÙð+­;˵7ß0D˜O<ñDl-w÷Ýw;s@·Â3¤  }o™ ž,®œ osë»c÷CN@½»÷u°Ï_n±šsì<“|êSŸ2¹#VØÙÜj¾æ$§Áj²*mÝNÄò ‡P,+ηm˜ÿ•  6ˆ2a/À=‡0㛀ˆ@ËJH½¯±´‘¶öÛÆqchí†Ó-OÙø ˆÁõV>(|9„¤¢¬Ä½Þ „ׯZLÙšÅ˹&Û €Éq×P[²¾ÐàC¢‘‹B%¢ý!yı~I‰íCj±hd42ܳH' ²ÓC¶.zð—pÙ‹ÎÀT×£ë^l»wí¶»î¾Ç=—+|Zu?\#©´cˆyfDz$Á% ¹‰D F/¬xꪾ®»¬#Ö.–þ“^%•皉¶ÊJ^ÓQùHÕñ—$»šœ—ÖÔYOKïeRé‘`¢Ùµÿ 5U3py¥‹ £Zpñ¨èœ¡{Ü¡ª1hbûêÇò˜jn½)8Íïo6»k“Ùɬ¶õ÷ÃØç€•¤×¾ØìW?øÝª˜3b9 d|ÖCÂ:Â-îN’¼ ¢Ï€9¶)X ³cŒi š¬lV›³qô“SPÅ9Äs´GxÑqÄØSc=|t:LA\Û“l …w¾òºS¬b 2·y‚ìÕÿþïÿÞÁÿJ d0Äê=ºJUš ……+³BÁË«`?L\/[¶ÌY lÞÙ‚r!u÷Föʇä°`‡vãI0Öt±a+㙤Ø("/³=y=¬./´!ÌR«A ¤gqY-–£ˆï1‚¸ ‚{°»ÃÖ.ZÉÐɲ΃‡0×›²ÍOm¶§œv† NY-h#(f³5"óÉR¤c¬öPÅDqå"í÷óޱõ°= ÞYGÂÐÐ(´0Æì¨-7›Ìµ•‹+¬K‡†ºeX“H`påçf³=i¿}l—­Y½Úºî}Õ`œaâ">‡”žð©š'¶I"íZ;®Ÿ” ¿7ç¢"óŠh&ðyºTÄ @§s…h_Õ•—lw®ÊJs…R™:‘}½mVXYnÝ@uD´sË$®¦§lÏýô§ò|n— &Æ…D[¢¢óµŽÿ9ªê‚6ú[Ž­­“/êmÿí±ÕÖúö5ÁÙÙkB<íÑÜÙñ׿ÊL>ìØæcÇv”÷»ïœÕÅŽª±†á¨*má àŵÍËø‰aß/¿/9Uœ’攎aa¼™Ÿ”ûáWyIcEò»X‹¹ñ­áW`‚]Ôe¹áž¦©V›K¥7•¿°bÏ `r|ìh·•s›8bÿl´BsXѲÔ0øßˆ{š ä»É”Î#!Ž©ˆãÑÐv "'¸Ü¢ªÕV» yܽAΞ|òIçðž{ŸÝƒN ?Ñ qº¼þ/âmmè\Ά$ PóW^y¥“$ìà0 ÷dÝÍ;lËæÇBœôÄpêf¯«kÍ[~#€<ÊIÌñ$†wV£ø¼Æ#`E£UCJÁ(€PO ¹·àª»o÷&+?uƒ]ñÿ}Âpö lçö¶Ëã83*·D]¡-i@aŒ•½‡QÞÑÏÒ¡_¾´ÆövYCÅb,²€#f 1¾Vó¥X 4Ö,qïAîŒw/ºq8´¤kA¤ÙV.ݦÅqÃmKàáÍ{,vÂ"gM0ºa!eÜöO:ó½&Ï5Ó_yôuÏרõcXqê‹¡H2®VXÆ•sìÇŒ )Ãe×P;‡ >{Fµ ŠÀgèx˜RV˜l’k{&pkkyà€ ¶#áÙPa•ËÙú*†±¥dÑ'Š–HËÂ× ®¾~×_~·k„Mø¯ÿ1{Í?ÀDì7»êAÍu!¡5᱄w0¨u]Øîœo%MƒC«yDü,ôœ ¶´+Ÿ½PÄþš-§Pt›!0ø]u–ɱ>·òã' C AD&ž]á,ò°«¯]~! ;.Ïÿh…ÿå/Ùîh5$Á#zÜÕ#!PœÛn»ÍÎ=÷\pü×8Æ¥Ìý|Ðô´ú—„Aèycâ˯¼Ú>õ‰YÏþ}@³!Szø(+ô݈|++ó­*¿Â‰ó ‰9>6mÍä•¡Ü÷Ðd±Ðu׫Ϻ„‡¨Ï´mÛÝlmÍ›í@G‹ö‚ÓÑ)²‘q ®Ù†OÏæP «ËÆM)0ˬø…PXˆ¦?ƒ&àÊêgù04Ê`¢weã ÑÃ>\a¶ Ì4ˆ™a %@0sÑ;0ì”wïeg6°e€Ã¡Þv‡e §NÁÈå7$²Ibí§Ø*¦Èìàë%sD-}G!>+ßL•I­þ“-Ñê? ó\×åê:¾ ?úkûúÉ‚aÂáÚ‹”Õc¨mÏ étñŠU–_‡äª©Âö=Aßg_srdÂrJÁÅ ð¡šàŽ•Ô¹‹ÿŸþüÝÛ‚Ë7…ïðìsîv^ý³Ï@dAñ¶~<¨ûG¡ànæ[9Ò‰q釿Í&—¼ÁìG¿M/÷oÿdv: Kx–ýË ïŠ¿ Ž^0^cî3'ͳ{xî2¿ËÔå«‚«Ýø£gñª™4®Õ? ÁÜãöþEÌs‰«sÐÛ¥îâ0"ðê÷ް«qIX€¯­ºNª&i¼ª)?nÃä 1U†ù£³si .Èp¤ýü˜ÎOÖÏöqrtpû9É4ý‰Á=L~»Ñü§³$Öéß…T >hQÌ ²pL XL!%sA‡’;Þiœ ¡cZ ,²ªú3gA×b“#×À¯}íkç!&é¯N„_Î…ÄN¶úº%Ž£!“BI6=µÝ®½ö»6ŽËß PöòYi—€Ã)}ƒM[÷ƒ®\q&+ôâ¼|ø†xž„íbû ‘ûöÖ=nËkûu¿²7¾ðrÞD7šƒl,¿ÿN4¦°_¿~U/n´2Äú­½¶Ý„|P ·8´À`v6LÈVÒ5X‚ a&L ‹Q p÷,¡ƒ"+Ädp/Ľ¡²Þ â23ݪøz-=îuìïiqz /Y{†`ªÿ8B§#¥Q&¢ô´Êy–z³ž@ú—O5g–MkKt _1í¨ûÐÜ=Ñ9Ãbɼ¹LfÏÛž»g ÐÁ·Æ[vn‡Ñ¥OiXƒw1‘i%õ…Ö»ØF»b¦ýÿ iH/Ðà!{™Ò]OëˆÃ|ç¾8h"|«öàýÁùåsÄMÏ*¨·¿š]ºO`þÚwÀ³Az±ë?XÓÁ ­?<^õ×Á¹¿—ôsŸ9GxaÖS÷Í]æw™ÊæÂÍH'žµÕNÓZ(°ëœùÈ}ø8ÃÂ϶€'Ó H 5Û¸q¦k_Á­üuäO.eœB¡â$¸4â³Bôë0‡Èû{ ~>y6޳náLxV€gð>Ÿ®©AV‹·§>ЙF þ]ìñwÙèÀ^÷ý'÷ÑIõ°  Î2=ÞK»ˆ„#Í1 ¯€ ö¿åç>¢ªý¥,t/,ƒ…åˬnÅùì¯.MÞÏgœaMMM¼F COd ›þ™ÄGÊB|ùË_l ¸·Šª §ø«_ýÊ1"hçžû"”†N±š³×³ân´,î¯4@1ÛšÛ­1ÿðiöØ ¤p½ôh™ú-[\míýâ ¯®µå'±„ß¿07ßúð%°bIµílÙkÙà ên³ÆúÅvÒY§X^qž=¹c?Øy¶³m[ˆØ¦ðHLôÆYñ˰EB!Œ‹áã“cÖÖžΆ NR\_Õ÷šFm%ؽC¸+îs÷40Qgß!;}Ù‰<Þë‘gLêèqúÈÔ7gººÊŸIðƒŒ¿ßaYµ– ‘3פ¿Vؾ+¨¸ÖÓ‘4w_©VfÅÔL¤éYùѶffúËPß]&r®jz-þ^b ™Õ,+´¼‚,ËÃC!È“ñXz9V\Ÿë`€ ,X`9¼ý/Ù”ü³×™Ýòs³W^ˆûn†ËÎAkÌX©ûvw­ùò†ÚR9}!#‘JùÝÇ@wáÎëž½k'b]4„;󞆃ú¿¿“ˆØ;%>Žÿq+ÿp¥/“?)‡„dêô”¯àQƒ35|TîŽJŒ$ BZ¹xž4’¹0¢ÁÛX÷z¸»LddfI=í á¥Ù¹΀»KL !æFRÃÊ>¢Ÿ¸B×yø™ëf"…ՀЅ$(¬€0‘ÂÐu BÒìÜr+¯]pP#JÛ·ow¦}2û;â#„;ù˜+üèG?rºí˜ ÞÿCð•ÛíD€½…8(||yÔ¾x n†å‰0¶&ÏN]yDvÜÊ‘Tâȼ¨l‘±~™ì¶Qˆ8r lûÅãT£»GCålspõÎ'm`C±1b3óØ®Ï °ß–ÖUa>Øckå:Øa]û1,²S^°åÀ,˜‹r¼¶ s²"ørP483°«£Ù1qY~®ð¸ÇÙ>˜ŒhÌxb‡cºûûluÝr׬oPƒ¢"–gÚ‚H´£Q ƒv×Ö‡­¦¬ÒñîzOzÿîÝò­±‹\OÈ) ¢ÁÀu68Mþ* òž©Ræùuí…•’ÔÛŸ’ˆyjÎNžë~\)Ÿáž”ð¢-ò@©IÉWKÖêéÝ tMZAE®öaºÙ>~C7[WÀDƒ X±¢ÜzÛÆ&hyô„ñ£Aà[½nvsI¼ã™ºÅ³ËÏL©jR¶>…§½MfíBœÿn³=½ž7¿Âl㯠qÍWƒòoýËô–އzÔoHµ’²TÂÿAì×7ÕfÓ³âZ, árâžå?Hé¹í€8‹:ÀÞ˜Ä9€@/Ç)þá –Ø´Ãÿ—D€?æœ`›À? 0BxNôëóSY()Kñ\xõ©Û<ŠØó…°‚âª;QÆØˆfõåÔ&mlä¨y­¬TùR`@O‚û?5 K Á‘Å@ hÈŒÂûÔ™9ìõãHˆŽG'ÀÁc>8¡íú@^ €A ç[ií{øá‡í¡‡rÄ7jòç.=Ç|Ì')~€Pð´­pÝÏîâBK1‡Ûe/}éK핯|¥s2$)¤·ß~»Ý÷Õ›­®¢ÆúFAþ+°¥K—Ú¿ø¶ªa‘õÀÐ,^»Ò ¹ï\þŠèu÷¢X†¤ ó¿;í§7þfw ¦š·½ÐÞÑåàƒåò·®´Ûü ç3aדÛ->³¦ëlÅÚµNï -ÿÜöJùOPÀ‚]ØÖ†§A”§Gñˆ‰d&fƒèd£„¶³¹Ã1½‡¬ÕëL­î!²…ÌÂU(æÁ0ØÁ›Ìµ=0+j`ø>4¬ï˜F´CŠ%æÉüpŒµf_NG÷7»ˆ®¤†‡T5\Àeù|Ýg@R…Sm„÷”ž¥Ê©ö‚k*-lÔ8Îj:RMÏ雯mª„‘•‹SétL,ÉU> ¥(h.ƒ)@"[¸‹•ô×w”g^2»‚õŽ%ˆ}?ƒönüMPû£Ÿ2,xÌþãÈú›Øó?in†g* /ûœ {O‡~6n.o {½TZ5©çºaƒ+8ŽüÅ‘ä:P¶B%í•4 “Eޤ²!óäý6\]ZCú5ßÍû"Œ{¬°šç+¶ÒõäÏ‹‹gŒð¹oæM}Á ¦ßi”;„0>z&°¢$t :JœˆI™FYõO²‡8NOB…ä­ÌÇG:ÓWLÙ”ÍF!°¤j“l£555™pÿç#ìÑ,…:®¹‚‰ñ…pËPe.ý6TRRâÅH`Ó¦Mö³ÛnvÊrÝ8ô‘¿µµÕþý½€¡a+ ½Ùb‹­}F€#vñKã>sÈÝø¨]ú¦WÛ¯~x=z Ùn^ðÁ‡a9ƒ“ŸQè3ˆþþ]û°HxÒbµuvÎù/Áup¥Õᤨ‡ByÔ͆Qjíﰦꥼ; ¾éY8)*pI: '­\lÛ//.u €L%’Ëâ~óŠÄ@®[²ºÐÄ$TW,A÷ ˆw|G÷¾¢¯-Læ*îٔêÌzÇa—Pzðϵ˜Rô½P*5¸]X-Å‚#¿bf† hPM]a_(z­¦»>Âu¢ÅWQÿçš ]]~/Êý#¡A!4‹Á6õ7ð0¼DD\½²ÜßÀïÕñÄuó?nwR{hî2ÒlÇèÅV°[öîÏ™mgÉþc4÷—6åÿø<³µtÕ/¼¶yî6”ªUñÍ×Ο¯œ£ÕäGÐ÷œ êžÏjp{÷Røƒ CÈøå_ùœ3H˜W´òw¿œº¹€1 q Ž&þ£í„) ú¥ùä %õ>œFtZS™ /ö¼av>v[­ôÿá$yâè7ÔJI~¢5{Nb(:1SìùK”$±KW·`”“;ɌܸElá ªÁ Ãѱ„Ö7¡­˜‡ì¼r+¨\gç¼ø2“c)÷=]ð¦š+RX€ÑÉ »å[Ÿ°~öç%ø$΄¤(xá…âÌçl·°‡E‹ëY8#æ…qxãßh­ ޲uÑÑÓCó•– .ÀiôB—?ÂöC>\p!Žzj-¶e«N´'wmµÏÞ~-[9¶¿­ÛìPüÆx'q¸â˜b0ú†lÛÖ­˜ À'bû†[±|9 €x8|(+äA$ åµàû¢È—Í^ƒˆ{º™p槬m´(f ‹” LàEº\³VT[Gÿ!éïEß „AJ½¬´Ážh¬†DÝ% ©fŠ!PAÂU Êx&!lÉ ’FÐ} ߣԛ‚¶\1÷ãóÂftH^/(ëï?R"ÌPA_?¼VÚõƒÛI^ÆåèkꑇÀYJ&ù™Ö׎rk.~øF›‡¬ližÜ4(ý~/Ζ-ó]ob÷mé|û­éçÑ3¡ómFUè±fwÀ“ÿÝ[és¼Šý}6ý&ìV>á8Žß»æè*‹©ûý š Ün!®Aá:4:"òÌë ”ºµ°ÐŸ›ð )À8QNw@:QÎ$0@®1õ¤û0ç !QŽà\Ú±/HY˜¿Ï xý±Çù0 O…`e~/—Âçb5ßÁGƒ§sÄ3‹èG}P'‘@91¬P£ŽÈ‰ öèl LÓá„°&mzý›$MRƒ|ý5?ºß~ò‹Íˆ¬£Ws—œõ£=|iûçç³2Ÿ# âøóŸÿ<Úú¹ö­ŸÝo;vípŽs¶B|…! ÆáOÿôOí’K.±ƒ‰;ùœ˜4ñ…è ¬·šS^^ »y£½îMo´åëQáïsÿ…¬ÚKË+­©q™5-_fËÖ®p×ëèêB{„áŠâBˆy>Vy8üÉÃïö6kٵŦÑð¿àÅç[aiX%Žèè·8[EHPvL° •ZÆL”¤M0 Û‘L8Í{ÈÀDPÖ]( d(Á‹¶( t¼¯†¼£È@Ô{ˆ½^ïšèÄøš8ÕÛ×ßLÂïÏ]Y÷õ‚2AzäjÖ·¢æÝ¹OKžD"ÁuÝóDJG Qc>CçiAWJ]MYÁY*mf.'ÌÖA¯ †œŒÿz ø§€µFÒà q¤†—ÿ¢J¼C²jšuy]ð9ü=ÃWøîgAß›Að?öos7yݯÓÑùôŽ^s&J~ßâÏôﶇ®EÏ{o€GW{á•Æ˜•!*kl»;Ïœ-‚Îê!¾–„ŠÚs>_ð.¢ñ¬mýÆXú]x/± êƒdêúí© Í/.ˆ¹p9¾×ÉAZW Ñ“©éÉ6Ÿ»Ï+`ÿöû¦‘\ë?‘>Ÿû¤ü¸OŽà©‰>Ç"@¦ÛËP¦$ZÕgfc"–ÚÒ@¥\i¹L`Y î2 ¡\¤j5 ¡AVéYE¶¨é…– ¦þႈŽôæ“h@(§žzª½ëßßËÖEð‰d(0 1’ûá‰qLQ»• öË¿ûÝïº|›l¶’Z´øïÙÄÂù (b¿~&as½Exýëlo±1Ô†sØ{¿ø’‹ô¢«wXßAú¦Õ7³ P´p O€$HÆ4ŒQsË!ÓVÚöÍÛlºkØJ*êlQÏ^Së4þá°°H(ÁÙ\8H‚RìÅKWwÏ0’‚bëc[¥¬°çEpÜ0\Â%Ð`Bp³³ÏíaOpM8*C’ m‚Pàî¾a@Äõ]õQƒ*¦À§û4}÷ýyß~¥ï˜‡°ŽÿF®ø“ä‘Tßg\+É "á5]qõ©°_%KEu7AN4-OVJ¸ÛŒ–S;Á¥ƒ{Ó¥U…£¤ñQ¼,!¹Éš²ÒUÅ8µÊU%VÙXh%Ëâ´¡·Á~éZ}.ˆ…÷¯i¾r­²´¦•·ö;á39òþÖ®ÿŽ?K½gº2ÌüôÉŒcŒ`m{T2f <ðcwUµ…Y¸æ^‹¯ù4¦ØX6e•žÁÎYpñE0y–¾–ìùƒ„W Üò à¶`PGJÒƒ PŠà̲hr•ô6Ãà˜€èTzäœÏ°ƒF´ æyŸO³Û iEdäÓg;b>>,iÖãÚHcâ„#L ü'ÎPæ€1öš` Ħz‘c@«V8?0ÔâèH™DÜg~ùj[´ô…–‹FþáÂá$ª§=~éÜrë­ìéÛ ²ÐÖ€‡ÿøÇBø‚Ÿg,ÕmiÅ"þjœ5ÂO„DÔÈUUÖ!q ˜íé%±1qüz¸ áȉÑ)òêfXÿõ‚©ÝK‚¼ŠùŽÐõGbî3ø|Ç$D˺ܙ?j‹zAUâ:ÚÆ]Š/ã)°oʧÏwîÓ]»*<³BX ™œŒ· ÓÙ·Ä–T)ý Oµ¬x½H±F{GmvßîqiÅ*`=—äõNÄ¿ó¬Eø5Õïá½¼é]‡.­šå·þ–kÍ.:…òïDlÈÂò¶{Aãü«TÝe”ù>ef¡Îúð$ ÏDø§¿5ûÞ·­¥Ù:¶žÃµÖn‰Ú[,Ñø K”Ü8[ý:Ø’ä– XYVþbt\èûÎnˆà‰€ `¾Æ*ó;ÊZÔA©Œå3wnƒÃ,\¥È¹FÒÙžžcb[¸a!·}Ûû·Ýߊ& õhî›…Ž1œn§XÍO°"Ëä Möq°ä,H{ýN„-€{KRœd%ì‚8F7œmÃí¬Äûh6bm™V,9“ccPvŽ_óÃé ô÷÷Û·¾õ-çQÐ#êxõÕW;ÔAI´çÇw ùš² §žâ xFÔØØh÷mÜÞ>âÞú%¿¤ÊòÐØ—Ÿ V÷;ó©”§¸± +Áf¿¯·×n¼ñFG4õÌr\ ȧ,³,Ü—TXç …@éÛ×zȪ` ¶±—_€¸ó^Û½gïtÂNX¢íî<`%ˆÙ ±ù—@¦C[å@̇˜&¦_æÒ*LÙ2pv¶Ì+áÜ\ x˜zß ˆ‘¯O§ïÄI¥E_¯#ð*åòU&üèa!Ç„q•9lHËÖÕuýTp)® bÑŒT‘yb¾°ŽA»óLO¦¸¿œË j’ïPœD)\f#Ñf¢Ì`+`Â*WZûS(À2JWåã¾:Ï^÷OéM/„3­Ñf*ìýÓGÝ4î¿@w>@Úá‚´o.|­Ùï öö¯þ z í÷ÁÏ´XìÜ üYê8Æ*] ‡Âí7Çãý½á+`\y¼­<ê'Š·˜5þ€¿ï[¬à1”ûÛéã8lCzª½d61Üùjß>ЛCfÎÌäõ5§Ômï:¹öý1Ì»ø˜!VÊÉβ(º]Ë‚ÏBsA}Èå=ýØd¾GÌ™Ò9 XX¿)*¹°îû°wËäþ:¬^ðÝoø=âª>%ÔÀáûÕt‚¬œr1kCt¢+E6ÍòàÝÏw¦‚ê„ÓàëëNAU«°&‘ ÖXuÓ¹–_Y*¤.éöôçÓP1Ý£ÄüÍÍÍIwÂJܰgÖbŽ'óÃMû¶ÛÒåvâ©'Ù Ïy!¦€ VXÕh—^üRjÌ©ìÀå´(Î~¾€Òâ"›Â“`ã g@ ³Ò`@ÙžÀ¶¿,€À{–”ÖôƒÅ€=eyÅõŸF{ÐÀÂø•Å–Âè‚ÂÞfw°aµ_ÕÄ£¿sÙø+ÿ“ßeûŠ—uÍõ˜òeö•ÿ†È¯7;«ž´ÿH¡óù¶.{µ¥ŽEf/xypþįSéˆï´DÍ–h¸³‹Û˜poŽY°Åz‚­Y-&bÌÓýH«¹ÛƒXmÅH›ò(«y™y+=—d  +­eŒ ØG¶p-¦íYHžÛ*À<0˜ÚŠ ¼êþ5Ð4šôw˜À<Ãö$²5¼²-àð¼d&ÇG…¸©Â}N÷=ùq˜I øxbä&Ø{È̸<Æ­¨Ødû/=€q$Sh² $HvôÒp"&§#(VåæWCPQ.ÄÌN„Ê¥gá¢õ¤YÝCh€‡“¨‚¤B”Ò ëׯwXÂ.€¤‚ßÍa5ß3r°ó #ª»›[­xÕ*l’xl­ïjÞåÚ’ÞÀN—âðg| ÝºY­ËÿŠ+®p:l$Ö´¸è߃¸*^Œ£Ÿlöó¥ìÈ?ä÷ Z‹9Ÿ¶– ñ_€ËÙV›#ˆúãÃ]6ú嗼܊Ë+h1gߟIeEňûêê1°¢šw9 6Á`BG|yð‚`iû¢—g¯Ã“¡vé¢AßÓ àà<•*éÏtÏî<üæé-ùV4äS5‚û oVáT‚/íZ˜©H+”ºÖü1µ¯¿°¢;„ñðÒî„Ïâ¤ëòa6}Ë•lÜ.€ü—Ûçšµy6Â6MA- œà-êO î@í *„3ÕþÝÁ]ßYyÿÉ•©'aÌÙøKƒ_îƒÿáêTþkÿ¾ÛÌþôefñøa&žL½ÃTÉ æá‡£éÄ ïÙ.Ð]ÿŽó $²ö0èfÖÄŠ?g­ ñ4o6>Î1ƒÙs›æZëbˆäY?ðâÂgÑÈaûRX.ìÛBøAÏ,Cª«›ÊdNG¢‹²Lt2¥;ÃÖ#›E1çü¹"çg̱1× ´÷;µ0oàºÓSSíä/è.ð¼dÚ÷>>1¿N„Ü}POøýlä¸ÉSIӈƆPŒCÌmèH9p’ÕpD} ,ý,V²Y€êˆ L€)06ÔCA /Ä::ØÛÃm\FÂA4ýÇÙ—*GInLÀšðjÁÁ¯âÓgœÈ\0Jü•­z]t‘ۯ߷oŸË¿ûŽ» ³¶ûñ­öØc9¬€Ch6%:ö1`zìЭOÚØÀ˜}ó›ßtʇÀŸ¸b12ÏéÜå$ B\¹q;ÏÜÇê|Ù’*Vø-P’"¶ Šl’Ò9ÈÀã¹Z ª­)wf+ª–2Ðð˜X(d-øìŒ1{âÑvrÃj;ýg3ÎòÝ*T÷^Àû.xY}mfû º¤E@nÀš=8GA'ò_¢ó¿Ò"kä­¼„<£‡F?ŸVû:÷œ¼®£àË$¾™ë䥈}*îË»Êúñ}%™à“gÁ5“§OñO+é[ÑQ†%ˆë—¼0›GpÁSÅÝ­úÓÄt>@SD09:À7Û C ƒ–ÇÂ'›íOº/VA; í·H >ê(.|ö_ƒ£~ßõþ ~&<ï_üQ*ýέxþãïï ÒDø×À·_u‰Ù·8 ¸Tᣈ@+.û“£¨ð‡¢s¼èfõ}–¨G$S{H¼Ít¸¶8R¬¬>3 ŸºYBýC2[0™À| “‚ÖEÕ³ß?Š;tÙOD_Ê}²Î"ƒ8s´Š É€æ @Ë ”ůJî2‘*dS~ÌÕá's.ý `$ ³p1º“fL¯ ö9fÝøÈP÷÷Ài¦÷øé‘"îò£ãŒïéJÁAŽ î§#Ò)°ÈB ~LAÛÆYªóħ‡,e+­œ3´o¤<”HòÀÈÄ2@X÷S`ä/ÅÌ™·¢þ €DN¥:¶Ô­Ü烞õ ‘xAŸþùîOúÖßÜn‰n¬ÏK7@Û/9ÿG5ÄÞð6³{šÓïøïÞˆøÿö² ýÒWGéE(ÐíŽ)\wmz5ÿmðÙõ‡pØ7€)uí=Ø\~ÏÅ âÓi•up£ìëËø0^Iãqù\ÉÆÛ(Û­y`²äŽ2çf1ßhŸÕ}K2Äû±ÌA®„5[Ž£ÌÅrÔ¦ «—A}Ø+xæ*`°¼2#wÌ' WåÙ|c0:tv·ï“,Є \PüiŒ.Ð0N~çáŠ-„¼ç5°ùþX´f¼U÷Ž„D`ö‡á‹ûQ­>@`ûׂRÜ`?˜³ecZ‚‚Ÿó!Gk×Á“̰N/@}‚Ïy€¬l1j“NIŠˆÿ:RÌEl^Zw&¡½–iG tÁ8E@ýMr·•]i…á÷,]¯}çGv߯~Žèÿ½ìe/sPÅ’ H;¿o`ˆÉ°ÃZv?¬'uí¼å-oqЉûZ:Á`åMÏpn„§ØC°iÀô¬‘¤‡ú[àfT²&seÀÈ€KЗ@ë° 'å’ ³UÐuè µnÙm]ÝVP¶‰@“CDU,¤% +–É Ä?;¯`–Atñn²,—‰¡±‚Ùþ(‚'ô3«øí òÃí û>œ|D6Å8øTW-z¢æ¢ç¾ÝYG_hÆõ5ù„ÁwÕäýª ÙºE•ò-¨x P^} © s%<(Ò€`õ[ˆÒZ~±YÇ^Ê@A‡X\)\þ§ÁQ¿+ONÅŸ‹±‹.î껿6{ÞöcŸ¹AÄïýw›ýüšt¿ÛlX”Â|ïh¼’ ?BJ ~Ø^ûõhêâÉ7P´Ýl1J}K~âî60–Dè¢È0+C>?dšq/(XL+ ¨‘þqæÓ~¦LÀØØÿÏG*GÚZX„iܦÿƒùïïµ¢OÐÉax3`´ïŸ‘YоVcµaæÝ"íƒÅÜÆ¼<É‚ÅÙ$ÒÚɉnòCÂït4š"þ.ÊÈ “‚ ÿ›>âP\ôž× €¾Í¾m¿ÆIÄ•Dw¤¾Uø1ýr*•AGpÿ]˜‚CÇ“àØðAVíÃ(› éÁe¡9êÀ€ ¨‰Y½—¨`lœŽ&8@'„T%у ‡¥D(¥ÂÌ\fcˆÝžDëUË¢W~Ú¸þ<`/zÑ‹L £(-þòËÿãõÞþö·›Ì®»þ‡öó_ÞkKêë­½½ÝN;í4Û¿?«î<Äôý{ }Ç èØÆö‚ ÉA!¢þq € (Sйžd¥yeVUÀ–ƒJîÕaÐ~µSÖ5ÚSˆø…802Š×Aii¥3p‡¸§*Äty¹ fX.ûrû¶í¶ûÚh«×¬¶ú–YŒ}þ3ˆ3ÊFh<½FtP‡9ž p§þº*¬²}J²—¶ ²”î›÷Çd¥¹"òû£šHrA{¾) .2&œä#ëוæ²òÈ'M«YH•ÕA0ÿ‹ » 38^|¡Ùê3R·ìÓÿ/zÖe¬ü…·Ë¡óýðûxÜ;7XíûûûÈ{|lö±vq†!ÏQ…+ßßú@zµòPâV$Úþ @T«´¼u?´ÌEÛ˜#;Yµ‹Pk5/±>bþélëÜ·›~‹r**Ì#RÆK$ØßGñ/0©gkÔQ'ê¢õËújˆvÐÔgU—zñŒÊkñ"âOƒmL[¶l ¼m »…íÙ¶ æßç$¶b f…™"'ˆ–Ò(›$•˜n&õ({ÔÌvþïÏŸ÷ €^që· °qJ¯›”“³¨ÿ¸ýDKy—fI¨4€°«M¡í/Q¿ˆaXõlž†=Š%Ô‘øœ]~þ¤u:F~¡ŒQè¤âNÙ `fÎʮą.ÊsU«­¬fÅQõ¹þþ÷¿ïÌE$þýê/¾k·=q·#âÞ¼pphÈ~ËVÀ}è ÈdPÛ—_~¹­[·E¿^“æÕÒàC½€/~ñ‹N‚ ÷SV’oMõ¶T?«Yä˜19ÓpæbB$î?qy½íÀyz}m•[½äº8 ¾ƒ6˜‘˜äfg CAþƒ> HM§å”°<­Éµú†z"ÇpW¬í÷òX°<wA(Ãè ,Eà?×\/Ê­~ÉʤJ&ò||výð#û ßHVŽoÛŸùbþ˜ºšOq•‚“93}9ÝH!Qk꾸Ž>£n3u«¡øŸ"#XjÈ;\.yÅÚSÅ©" R0,´t”nç\@•ÿVÌã tÏ+1*×Ü óÎ+ùÍÆtt¾•<ûÛ_Fÿ½šþÍþìp §ÌN<)È™cúŸ¯ŠKïƒl~*|ù ÁñâWÇÇßëßìVvúýìï߀„~/%*{Ö(ª¬Ò‹Ë—ð­AJœ ­ýx6H˜9Ç2Ù‹—hž­Å,,†¦§²™'ô|ÍaZh¿>ÞË—£¹ g@=tdn-íþqˆü4J} ô®dE1'mifÅßF›p¿‡ áXTE?4ƒ3~çΘšÛJ&7¿°Ãï OÔºë‘ûÙ x#Ÿ“©0’3iøå“ q…Gåx ¨Z´Kt´8¢§šô‘ÎADž‰]éD8SH,/ÑSb1 ML 8Ç€Èfõ? Þ@V •tô¬œ2V»«­¼Ž Ñú‘Ùþß|óÍÎäϗϬ.Dßo‘}éK_r„^éÚ؈ÕÀm·ÝfÚ^€ „¥ ˜YPne¬Ô—Ô-‚è8å?IÄ<{_ûýË—.²›¹Ëb…åÙ!”CÎ4ä#¡³»×Ö,$hxÈV±—/dÁ€bgâ)°Ô†À9XZÎ’Ó¡vé=‚ôÇ;ëíàíÚÆ‡ïgÖî² ^rž•b‰D Tl¼zú`0ecÓŽ[àŠΔzÂàû¨\êÌEÓ~Ò¿bZ'~pûtJ;ê:3'`´TJ5Ô¦?*-•œ%ï×_ÜÃìôÚá=„‡4D²|x hË•á˜ìºa¹¢2¶`†¦­éT­¢ð¬¬m¢ñ^@n þ -šºæ öÿæÉx–“XØ¥o4»&äÑ}f^äÿ™ÿ0ûÖ×SME]ì¾›ÍNF¨&f`¾Ð¸<È™õæ©€¿«d¸åç¹àI‹Ì>ú– ùŠ«‚ã‘¶—lìy)Xóÿ³÷p’]õ↑«:çÜ=9*'$Ñ,&ã€1fw ûìµýœžw½k¯mxÆ~¿ÅØÆxÆÀÃ^0Œd!² !”¥Éy¦§sιz¿¿sU]ÝÓ’f$4g¦ëžð?çž{ï ÿó,½ë ¸¿ÏÆO£f ÿ¾töÓ¢Õµé+!|Œ_”êýFų/cý< ç»$½}ÌRòÕøÓÁ|iqr>mÈð$zi\1j‰sÄ‚-à„õÝÄ¢>kÐÒ‚´ÀrWd5”+ˆÆòâ¨3ï¾¼™ŸulCßÇMähò…õ $³ß+¿%(hÔôâÛu.þçtOPVQÃr‘ø¾/Ëbô•ÝjJ\«©¾s”‹¸ºÚh–ð°0¶‰t¿Èàr/œB> Csò(Ì4 i]êt2`¶¥"g¦Ð*˜@¾ûP޼@Ò– Õ·^ ðêŸûs¸8¼üyyös}‡¬1>f»÷îq›{p7¬²i¨2(äÔÿ­»»Ûî¾ûnØöâ^gõ%£Öˆ Ôÿn¹å{éK_Ê3eœ±Ÿ--vø,亞.~7»ü NòËPŽìwª„³“¶í†|̪?Ý ¨âgù‚N¬ ÎbEPd¾™yQDØßAŒ$´“bw>ŠÊoÆ®‚"±yÇØ(ª# 3ˆ;¹^æŸFÆ 3ƹoâ>Å“úɿש®ï¯wÍ÷è¢Lºd|KÙ²ìxÉæä÷7[^xߟ›z!´Ks‹?q]TWãÏ55:ŽÍ‰¤y *ün™ªa3Ûv È@3ôÈè¼âuOù¿ý»Eï|A2Åbºú¥fýoï#Ÿ0» 6D|ÃÿòmfoŠHÉ­ÜC!ö…|Æ¿ý¢¤â_£¯'äéܳsŸ3ÙHìK…oË?£vÿ8ë ­(ÉyO‘óñëÑ€~½SÁc3/[±ºV™èFøT›v- ÖÈýZÇJ¸–aÊ\õÝ’Œ€ˆQDä¦?dI@Ú™Eê”´€¯ÆôñÇîBÛdá-//þ!_Y¾jsßÎm¦|y}|]ùó3P ; RX”Ôqy \b#”Ýç H¨­°UþD)¨¨i‚}€M}‡eé¯Bê’nj2Xcëv€ë7„ˆ k‚!<úè£N6à-oy ¦vA6¢ _€‚NõúЇœóŸ»¾ùMl—ZÙÎmxòëÈÖ¹á†Üãw÷b짆ŽCÚë°††Û¶k›Ýzë‹"°ÿè| Tá9pÂZ0{,K\ãhØø f„›íP÷1,VØÈø”s4 ™NZR£\„ä߆*àÄø¸UB˜`‡jikµ—¾üeS6ɥϲg[«=~æ(Ô D±Â÷ˆžË_bß.›¿öÌß,ý‡ÝGÏo6¯y%T_!võ«hß"0_Í­)®êº?±vëR¶íånÁ“§÷¥T†Åïèwû¬i«üàéî ¯”£5VšAJ‚yÀ 8A`Ὗ9m5/.xA]µöf¿ñ§f@F¾ éþg~‰þ{1âó&¾;ïüå\:x–§š <‘ÀÎ)Ù ¾?C¢žþ¦;-±rHC]ؘ¨G•8‰%>ùðШò~U} ƒqŠ@?›>ëÞìŠõž< ë©…uS,Ôq…`Äe—G[hq½ü• gÄfÝ!Àº®SehI躾¥ƒ4“†%‡ œ±-M@ê?Íz‹A4ñ·6 ¾ 6~·ù“¯"WÍ«l{…iæ&7fº² qÄï`ñ<Ñ®÷z”Q”xßÒÂÜ{‘H–D?ZÞýRªVõá)Ë­¸$ý)p2öe„rò`“öÏÏŒÇE¡á‹ïú¼Cô‰úN=’èÚÿ§Kó³Ö}Z.Z!óB”'˜6ñ¥f!/ û„˜d£×æ^QÝápÁT©xãiœéx™€DvØ«ü ¤à‰¯@5X†u`ÉFîŠ)KX •õ[lËå¯Z°9¯'ë%´ìÚµËî½÷^g¨¬6Œ›o¾Ù^ò’—ØÙaúÉÄMØc=n¯|å+€œ U ;µÿx7êŒ8ò©fÕ&¬€´ì“·~胎0€?€j É<0Â8c¨jÇà¯íÚŽ©ßþ 8 kD °º¶Ü™Ö<Ÿ·Ã'ú¬ÍÓ=¶ë4³s èØC?ø!d ³Üú¬ÒζMÖƒÀ`‹ ¯»Ù¾Mô¹Ô±uÃá ¾m~“ážþªõí]ËÊÒxY5fb-ø ±Œ } í‡üèªì¢,Ònø)Ÿ?mýÙ´ÊC€*u«‰ð6£/9¯t ­€f'^áº:p†µê¯þs¨Ïu«Ú>O!4%4ö¦5ûÈÍd›ÿoþŽ>]Aúÿ3{ÉÕ8àÙ¹ÿÏs² áöñEéÅ/ ¹xëû\|£±"ÿðDÃmÿèk:˜_3þIÞÿùeÏ•T¢ú§ý¯1 ¾n¥õ£VÛ†*^§sÆÜ(Ö='`?V³Á7â0lqaØNLXUMk&|±ÙŸ‚ìŸ.+§¿(eIX8H[^¶ñ´¨ÐSº8ËTzÄëç´¯“}2Qí( žÊ mªú–6¹n?I=l—,‹úêLOèkŠoü¡ˆ&¨°üDɵÚusŽB44ªžD'Öjù™Ëϵg®ÏÌ165ñ®ùÙ‰¿gDñé5âS¼ Sa¡wƒ…2`—ð 03ÉŠ Ö*¬ÖÀHŇZDïT–¥5PRRj ÚEPPÆØÁzKA 晰ʘ4Õ t,T±*K å½rTèš6]C—m(žT÷%à÷º×½.5‡LEE…½â¯pöüå ãÝÉ_r×^{­“!¨««³áñYÛ„CçÁFg}áNç6X¬„eÔó*…°á$ªÒ¶Ès–‚Üj­˜žASb‡Üý"hXWU‰>™ÕpÏ¡£¸ æ8¸¿ç8TTa ÌCµ•S“kž]Pf/¸á&+Gqsc+Vµ•(¬ó}V•‡å Öø¡½ì&^¬í§k>!Û`ÉfÄ"E»¯P<êòçâ>šk˜üд¿†T ~袵íB#k‡ÙãßQ Hþ¬$Ýúq¨EÇУÿ'dºÍNÇÐOøfã= ­ån¸ñ˜”ÝoöéoÃrXdÜ|á¾çäÏžò³o0ÛAãúë° €ñ´©õÛ~Íë}ùÍ<Ó)úÿt„Ãßówyýއx¿Ã»}N…¦,±í3V±ý µí«¨ ¡>HÖ®ªzæ6s¯¡½Îš7ËóÞ §¼NV]–ÌZu“¨|ãÖ¼©’<ºù ´éËÓè"þ?j›[­®¹6‡ ú¤°FUQ#öTY^E4•B 9)±–ѪZFæj¨ûNü‡ÙtñxÎùŽ/mþŠëÏM2]ã3@íDiMÀl‘"úKÜ™ v‡°²^Äðä»>1rVܾßlêÜ;_V^ók  ¾±°êÆ›ÎæE0B°Œ•¿Y$ûK3uL˜r6G‘ô9bI•3ý¬JÒl~¬Œ"LÁ3K³¡ÍÁÃ_˜Ç;_`q‘Í>ZªÁ"º¬øÖƒ‡›fB]eC‹gÆÏÆ{²*®Í]êòß¿XŒ(RùÇ¥ï jŒ½]=LÔ‡HSàž{î±C‡ÙÃ÷ NÞZÍç ‡–_n‹5Š} ?ßÆÔ°ÊZ!åkß³m§v–G‡—ØAò&sÁ¼…R„vdÔGÖ{±Aðò›/Çú½¼—¬Î#tˆYNyíÌÑ“öÈãØܰ]ó¢ë¬¹µ Bš"v¢÷ï'b”.¡,¤×ººILa€WZIÒúÎÑ%W®§ð0îôÅý%ªë[ÈÿU‘n‘RpÏÂ>¨J®ÙÒláMe„8WX'7¦mà$$6Ëí7ø¶Æû ýCÅ‘ p©Æka*ÁA<±Ê,¯¤ø3ìq ʪ9àǺûñ¸Ë ›{áljç»_«&PA­hbPÕ SÉDÉÝ€mRKüó=Ì ÷ù¿æg'?À×#ùþ½d7þè5¹™ÃÀP¾Æa‰Q-SÁK³“`¹ÒùŸGE®A8™ÚÕæ.¾~™2¬²÷²VÀ¯Gv€¸ Y@írš©²Zš•Š ÌX–ZëÖ Ámõ7ZçWVþüq&ÎêGÕ†††ì¡‡‚ú€PbSƒuÞòz„ó@ÄÿW¢¼ìU? ¯…~`U‹“úwßç6!¿ñ¿a›7Ù<¶¶`úwÓ½ TùæyÆz¦§yL¶rŽ+øU`ƒŸ‚)]&=ÂÄ̬]±³ÓŽöŸâ=,ã"80á´Í!?Õ¨ öýƒÇØímìè´ ùà—­Õñ¨8{‰>L6] í8ÒŠ†oí²]†2ÝŸ6~Õ ˆÑõCá-²Ð¡ÝánþJ–Ã/Cä:¨¨¢®/Q»`ùìÔ£ó,¼l`W±Ñoö²rp)'@{naŒâµ¹høî·}ö{ÿÆìŸ, ’—©sáK  |â.³G{°»O½°ù ðUœ$?¹ÿ‰lþªWC”>áÁûÏG+¹6üÖ”K_L±D#Øá¶¶ÄöïZªr¶c 'r wÕþA®aü$9¥³®a9©¬µ¾ÖJøø?#À¼åòÝHÐ(Á×òì$}Œxõè‚¥XÁFŽj‚Y,.Ì! ÿ)’‚µYÇBÇ ¡-¡ÖÍ[˜jI`0)T”€EÔúÃ8Ášˆùô™?6ìÝkާãq_›4.#ú §ÿpB P]V5ä³]~¬ Vêã,pÇ£Ò‹þr ðŸpa¸÷Èï‚ü#oíT°a0DW<4 8Õ÷r²Æ 6*«ëqĦƦ"cb ¤p’3=ÉJ Ï«\Îu˜©ª2l¢>P@Æc.ÐT§yÓõ ¬æëmÎÚÀ× U¨ÚÉ€sD…1’¶ìÉ|øÃ¶/|á vüØIKlÞeÛwîÁ†À¸ýÞ»ÿ «RøàƒÚ–-[pÚ3`ÛÔë‡r`¥ P-x 3e £¾Þ{4œÓL`Vxd¡ÉH/w`xÜZÑ0ÈðÜ­5֌ւ‰cŽ¡päïY‰‡ø¨¡h ì^á /?\]×ùQ:ž·Æ"°À¹M?¤=€R~»Ôžßú³-«žKdspþuU±2Bf¸ÆîïoîÛˆ²Ý­uÉP%º‹Q&±•2ë2§ï/ƒ?ïåïDLƒZ"}͈IÝQå‚ËŸÎeÜxS.éN—¿ÈìÝðóåDè3wš½òUfwþ‹Ù¿2_…oý‰o5?¾÷ºüô“I=òP~­ùL~úù”òC{ÇÝèîßPÈñ”‚â-’“G5<5v´OLCò—rˆ&Í&¡b‹ëå×¹ÃÌ©ý'¡bÎÚÀÙclÚs8fÙ´gml`¶sD¾¼2m»ö°¶ÍÙ$@Ç„<@ ÄwŠØ¦“£½z$ˆÀâ)Sïƒòù0‚€gÅ`«¡`È”$Ʈ츟,~"iáÕßz!À¢P¾Ê4ú¹ ^ ÑXàÃþ÷æM—àÕïݬÑÚ!‹ …0prq?@pp1y–Í¿¾¸ªCB“©KÇËeS+Ic02† •Y¤]±b&†–L.×”+Ø XÁõ°,gàDã &P5‹ûÊŠ/­b?¬Tþö·¿Ýzñü×÷à!›X8N[¾ç¢ b+àìÙ³Î5ðÙ¼Á^tóMÖxãÍÖÚöyÔ‘ö¹ùÈG>Âu·½»­S¿‡pøÓ1Ñb ¨ñh!´ÿå;;ðø‡Šà{ÐÿMÛýf ïë^§ð5 $)¤§¼õ6ÉI£¢²†Ç\Æ Ð”õ ¡¤$ÅûYzŽ÷í.úvC÷ áýë{(Òë½`âÈ›â®Í¨º«êÛ;ñç§\¡/‡x7 ˲p¡ÏٌՑ$t7tU€>îÑ”Ð[D¬íèŽYû®ÝN@ÿ±ïsúoór:ᡜbM{@Ž®¾Ý±æçÅ{(evý <Äço7û.Êù;y¨õgÙù¸ËSoCï^¡áúqú!^!qâ5 ©Ÿ9&ã<:áë’HLÛpw?ˆc;BzÜôÇ8Í×Á‚D ç³ñU#06xƪj;™Ï36Ô3hí;êY·–Yκ ¿ª¾‚vlÕá©ñRæn?ó ê'™’”œ«Í°ö‘Æ`Z›þsÓû¿>e£  ¡¿.Qð³æŠàòÆY|Swq?;<(B \ƒñüø ]aË÷Ù P<ÎÓHеý F5ß.…ÜXê>ü^(Ä é¼Ö°d4„Å_uc`øÀ# &(¥ÿ_ÖõJþ[€»ö+ÓÁéÊ6|¬‚-g8í—V€ ÐFd •’7Œj°˜gŠ]A°°¼²‘{à\§ù2kÛñÂüæú~Θ É/€¼þåßÝaŸ¹í«LøWO¬mòr$¸‡Ûg]Ç!Úû…wü'ç1PöÆ9¡/³ˆ;=`Mxöûξû­ëþ“V‹#Ÿ46f‡ÆìŠ]›ì˜ÄÍm;Ïq¤‚G—ñ¢ž¾aã=àÏ{EBìJ²)‚ER_U‹ê`… Nbb»öÉ;ÖwÆ*$¤÷­vC—޽x÷”Žàp±Ÿ}Ã" ~«-ù{ä¼Ï]¤6Yñn "/ú®y…9ØPLõðèáÑ\¨<€é*}ì=·ì6ðDx®h] Bª…x"þ@h,[P$"˜Ní¹(?!+[#´é)¥à®®^TÁg¯ý*:Õ !žkÇ=^(ŽZ\õ¨ÊÏVÉ;?é‡äý| ÄÑøÆlXäÉþ?'á­}­ ÖºÎøR‘ø?þOlöÈ,ð9.!Å?ef¸÷èçg§~ÕSž-ŠCå `4’£ÕZ'Æ™‰>·áK@>ªeÕJ,m⺊E –åpHf‚!Š[}ÃÚÐäA*–['à£IÚ6 ¦  Ä ÄêÚ¯…%p••V²Ò?ÁpìØ1î £~7g/zñK¼ ¨ r|çA =ý#öc/¿Îæ03üÑ~Ä ¾á opê†o˜^Hà0]_„„®õ+ú+GÏÔ~ðØI«Â|ï‰Á^×ÎÆ<Êxö:Hür¬ç°kk³è;nh>L!ˆâQ—܃5qe° áÍò ÷š÷h)›Vœ†õ—¸„2!®Ê"®6õÍ”V±K+¢?|Ê[ Å’™ðu²•¸¯š«N~<àÕR”Ÿ}ŽèöjZ­ÐXÜÝVQU_C‰×a·Ù;‰6{ùW’5Àîýž°•;–§­r~<¨'m;0{ûnooËV_úÀqÄó³« @ÉÏû0Ýû Ú…+nØ\€R?ÎWƒúñ[?+æÙs;è%[²ä¶±Dí=XïCÒ^'k,}V!„‡ÇÏ9äp&†zìäãÇm¸kÛ1§Ç/J@"ÉFw½Šš2;ùè Nò}P ðî,pH‘ŽúæËmðìpI¬JÒ~»ý)ñïeÚ\ˆ²L°,K+pû‹`siÅãp÷ÓÀ~ø*²?D>ª‡>ÉÊßêÊ«‹òr\ݼ =¯8š/´–… w#/ÜD×/¨î’EÊ=x¢ÄåKŪ\Ìy—€µ¿ž(Ç„åo²’²#­d±ÜAEŒ35êkÍÄógó/GÂ?Åæ>;#Gl÷Ëá™§ø·‚Ià4f0gÑ&€I€¹€)(øÀÀÒ¶¸›?$(Ú’t.Þü:÷²  ùµú—Ÿå•WÚk^ó‡Hà/˜3V.½òÔ·û;fWݰÛ*ö¾–Æ”ÝyçN6@,‚ãÇ1ŒpbKKšDàŠ-»xž;ÙS¿'NÙJjCøP{5¸ú-‘<÷¥á*,ì²Føƒ°æ½užÏ ؘÁŒp[sƒÍÀ&¹ºs§ÇšÏÚà ŽÇõ˜.çMòP'\,@‚Ó_¬®ç¬ ÖÅ[ ´I†‹†:¡?ñ !¶ÔRì¦*^ÕËËŠî놙¿qöŽÎ÷ï‹lðøT29c”1 ä,6€4ÄÕkðþl?þ³¯Áéyà„Ùï¿ ÍRýïËÿÇo™Ýý9äóTxÓ¯š}é³?ø°KžóG‡žHÍ…ßÿoЧ`?<b—®zúæ‰ô%7}ÃÒ{?‡¬Í´“©± o54Q^.ÊÑ4ªokFD‡|þ:+­žÁ¾qæn’5gŠä Ú#il“4o­…×ûóÙq¨¬3ñ¦Y³Ž=|?iiHÀOŒ¨¸7G× múVôü[Ñr):h£ƒûA¾™ÿQæû4ðEG½Ë/ú“ÝÀóKÝÐÏžîóË|Š7Í—ζC¾^šB¸úTì7^1—íï©ôÊ¿ôŸy|(Wò܈]BÖÿŽKÃ}Gÿnvfì¿0’½‘úUðÑÀÑf ºp‘73ÍŽ;áy&›  ûÒòz'$'‹%lûËËX¿b€§“r»;φ‰ø B9Ò—­dÃOãZ¸J^]ÐåJÁOcw mÛÍHôÂkÛ`Þ›Þô&î³bŸøÄ'œ×ÀPU€øüÍÍ͸ b‘ñ\Üêô 8ø•¯|Åù˜‚ßWÓH?àí/Grª€ ýCãNpùþ–Ä Ø4ú"rBä ©^âžm°0Z߉ð#OÜà ;— ±Ðh]=ÃV!¡i,†íhŒ=_ök&‡—MÜ%•ŽâÙÍ9<¬çx]CÜW é\n.æ[Ñ¡é\”gáë]ãí©³ «ó²9º—r¤¸»¿®Q\ÅqÝ„ªZOÒETÉÈ,Ê9pEä¾ì–7ãÚöN´&!çÜìÊ«ÍþéÅÇìÆVøæ¿®–Qñ{•¿†ÞþÙ_úôÿñ+þz®ß×½ñ\ùåoz›Oú¯ó­ÞóÍ|¸çkJß!Ùxƒ=·±ùßcUÍÈÑ,—Û`W7Þø(åp1‰ í¹ñ2xò#œèÇ!÷£VŒªÝÔÈè[^(µî£;íDz”Sþ㿉qkÂÂ_I úýÌçJÈ÷£½S¶ù²&X“=Øë¯Ãºß"B‚ h“ c¬¬Ý:v섯/ƒg¨{~ù¥û˜Ã»¹¦þ†1®úvëmòÚÔ³»Z‹Zùa°†¹b~"°ìÕ•‡Ì\äêëÏSòÿ)½è³.!ø„£ý'>4;=ö[¸€òèl±:ÙÍ€F¾O´kÌL Ø ‚3òw -d@ÂtlüÈd0˜SÊ 9™D?Ÿºóó’†’ò xî˘^ä$\Ɇ»(Ý\,i¥RšŒ èÑ#I¿õF0úÅzµ*Oj|²ü'§A·Þzk UB™ þéŸþi, ÎÛáÇOšM~—S!»AÆ‚¾… Q¶nÛe‰–M‘ àk„_?ƒýïþþQ«ç´ O`‰kZ1,#HIëŸyF{a õR!ìhm@;` …õk¬œw"ks”MÏÎpz0;|ªSÄå6:9ŽQ!V4÷>}4;£•§wʲß!Tp®’Þ­û8¹,—ïê*/ü‰:—‘­’·ÀÄ¡~¸ª(Vœj3åÚwydû>æƒè‘ÔTöJý𘡟jqk,½ÇAªà˜ tᚬ#.mÉ€‡Yó6³ÿñfoø ³ïÜcö6½4þ{¿ˆåÀï€[RM…_䯟¾—Ó?›¾7‚ ÁϾu#Ð9Q ô,ñðíoÄSÏ¿x‚w²ý~Kîø´%벓´êæR¨ßâ@« ›sPéÚw6Ù¦=Û‘1*·œAh¸=\ïb­¯¢ Ácê "¼[׊@ªÈri^ VåÊýu­-V‰…>¹êëaµmWíb³±-—í†?ÅúSÂø€e‡üÒ™ƒsâ؆>fý]·1½Ñߘ.n¼ùTkå];+AP‹AHAá@*óIE€ð~޹Ìï'’É}E«_ä™—€ ~@€¿ÇÄB.p $ 6ÝÈ ƒJWÊø/IÛ‰Ñ3œŠ¥Û5ÍNþX!Äc îƒÑ«uVó´Ò¢2˜.ƒdÇ_]Ç2$ðw-Áœ$”Ùp^SðÖì-›¯³ÆŽ+Ïù4rôùÏÞÁ½ímos*y¡’(cðo¸þú›´¯?€íïä@y"Ü·oŸµw´Yý7À°…^»jÓnàØ9ŽÞpÖþœ`ÚײóìhÙÌæŽÜÏÍgì‹R„{†‘"nñ[X »T’äžAƒ¬OÚ;Úm?ižìÜÌFèLˆk×t;gø\Ãé¶NåàpÕSøü¨JâPJÇ •ŒîƒŠ(;ü9àð yE®ª›š»+аˆàAU‚™é*d. ÷KæRÃWí°‘PÓã´/‹lr ôo÷$öÿø#f÷ÜžSᣄ(¼Ñ×B x™Ùû7@ö÷ý0 ø¾•ýzZ“‡Í=­O÷vm¬ç”Þe¢²×’[¾fe—Õv“Á²¾|n¬,סåуwM¤øšYO*áóÏÙéýÄ)Aàà5m­ŸmWm…ºˆÉÞFXqèö Œ!@ýÄÞ¥O+#LT®Ñ昗]b ¸jª¢±T^õ¢ùB0½BOè¨bÈr}óðõÁß.ÞÁäl¤RMvÙ ·Ú¦ÜNhó—@ÿq¨cf˜÷ÈzŽy{~K¹Î@ùÿn 2~|Ã5ÃbrwìÄþ‹ÿÍ?G€Ñuû5P$.‹ç \øJŸþ÷?•Ÿ¯”¦Áó!è³Ë£cIû£–Þý«¹lªµ8Co¼ï{u Ì„9:d…&ûò{G~H–lëÕ{˜ÿ•°æÂC šIHþ2U~ä}ŒƒkE’¼$ùá׃nÚ³ƒ;"g„ã.‘ûåÅïôþ‡¡&6c÷¿Ñ:öB•¤lk „‡¾ƒŽÿ½6Ðó÷œŠ¿/ô3  ¹ï¼Ö¸Zõ Ý&^òµ Ø%ÆÏÛÒ·ñÇáÕu;¡BU,4ç²ÕWì!ì®|©8äÅŸæìÅÿ$OÓŒœúÌÌäÐ/d2x¿(º„•B~4…•œ~.0 'FO“f€·XY MHàÈä+Õ–±œ…m xx#ÜF“LÚeÜ /;¸+ƒÄxP +L —WÔ[^_Úw¼ˆ:Å?­&í›ßüf{ùË_nŸúÔ§œtxuÚØe@l‚rŒ°?úècöÑý‹HØ<¤ì|ðƒ´ï=p\dÔúùŽ%^^eo{íÞ³ÛŸé·w+¿MX'¼Ä vÆpÿ‹ócw0Fèo{g³ µ® AÁŒÜÖ!$(6‡ä$† "lnkÄHФâK¼TÇ‚{ákÇÞ«pS7šÁîDq_Ûƒèו咊…fý²–P~ðMFe«îßÞJån—­¦~(xIgÌ…Û*Ç/Àê!òKclúKNÒ_õÀ+í*Nï‚ëÚo,èä!Î*'Žë'ñ+‘<@€÷Þþ‚Üû eàŸ.|÷+È|ÀÇÿŸ?Çñ ~æ^ŸºùÇÌþíP~ýßü=_þoÈ&„ðÍõ1,D?§ƒFv²jÀRÛï±ÔÎ;­¢¥ÏªšæAÚàÉoÚZg3l:Nû|gLhÊÖ¾øýd†¦†— ñÀ™nNû#lþ°*e=‘þM»/çœ1‡°Þi¨2ë[km[¯ÀPÏ ;}àQkÙ‚ÀHBç-g`‰‡~Ö¨QXª™ÅÐ"¾@¾Âºóæ¦väub_$Äu s(V¼v4:ÍçÕ 'üÂZñ Þ•q7‡Wõ*¯±ÂFÔÃX/5˜CVêÇ·ò™Y+™ÇYß8еúÙs3ß%ž›ÏzÞžjbøìíh¼³¿ˆZò¨Âå…G±õ‰‘SC/ç®q«SðZåÈp ./oB\õ„ñØø%Ý[Š5-‘×SðÕ¥9°EAB…𔋳8ÕÀ„g}ëx¾?™Ž#BA˜…')þŽŽ{ë[ßÊ&Á.•éäã7Úu×]ç…®®®<9+êÁ½?xܮܳÅîè~[ùN¯+ºùE/´GŽœµ[®¿Ü¦ïÿ¶µ`c<‰ `ÿè€C.4#?ßÕeÖ€$±B nA— ýƒÝ°ÑK‹=ð*ñ¸ecp¤¢TðÝüξâ(Ci—§RþByÑï uç(hS Ñ—½ª„6C[ú†Š‡t®HDÝY¢öÂã7qê8¨Ø=ÜI_™1˜Ð´‡U ªžÃ¤„²Ò‡G¾¼=€M—#À:ß²^¾†ËC{G>à§?éÓßzÐìSßÊ/ûÉŸ÷iØÇöžß†ÀGråb#(|ô›þZøûš×ùœ8±é—ÉÛûœ…u.æt©öCVuý×áñßSQL‰£$c<“ƒêÞ±Mw$ A¾¨h³¨ïA„tß±û2Nò¬XÙ#ƒ@9<ÿ&Nú¥üw Eƒ òLþþ€“Ò¯‰(e.ãxl°û²GÍœîËÐÑ?Á’)ß)NúGñÌwHÁ•¬Kœøï²±‘/Z÷ÉÏ2wш½põ?þŠLl³Ïʳ×L¼]·™ Hˆ@ fU=5*†k35üVN®cÅS^³±…<ä/±±iBOsŸÅ50{ªd‘‰\ƒŽ>ž³p$v„SèpÁu~ÜY,ÁŽI ›æ(ÅÍ®( eˆy;Í…ŒUâr·}û‹ v†[º«Tð>ö±Ù]wÝe?ó3?ã\ –@vؽ{·C®¾új°ý"“EÔ‚ÎW[bûÕVN©é·7þÄO:¢¹s—U_ó#vi°m°`JWâiÃB 3:Á@öomÀÂáaK/T!´Æ}`}8ë`ÌÁ„  <$9ù÷C=h®„ÕPÂή2Q†Òzç®ßÙBeúµ |P\ˆæn¡œnk·È;ÉÕb®Z¬î*µˌǵhñ?tµ°•\ïÓiEËð'­ó2¼/²ÉË|¯”FT2ÞÏéáJ©BÍ=g f±ð¹Ìå¾ì•ùÝÿù·û2ß³?ý•¬bbüÈ¿ËåUã#äŠ8Ù݈A]|ÑDaà­÷YýM_¶Dõ£hj`¹Ïêá±W#È·•zœÆå]T~2ºs¼ÿÔøFºgÍýÙ‰iX…óHï×€, ­?‚7h#½}Ö}¸Ç:÷nqesÓ‹Î1™¬ø œ>ŽM€q4`+Žž@K$•`3TÆ æ]¢í°¿oÝ'þÞÆ†?Ǻ²Öbn°äÆœSúÎú[Ö:ÅÇLá&ŸmH€ÉÖ+ÈË›/eëf+D™~ŽäFk~EM05Øü»–—ߌiøÓñ–ž‹ñKÀSøªãC]ߘ™~“CÂJ]¬½0ºLq\%œ#ã3x4üÁv¾¬êT¼ é|£Ü2Âs:õ£/ŽMT~2)Èr\“ðÌ);ãË˨²!Ëû „çš±PY¿-¯W:éëäÿ| Ë—€Yú»ãŽ;œA • ¶XðÒW¼’¢¶iS§õžíÆNÀi;yê”ýÒ¯pü³f;;8j-õ-Žî<#—tѼ›8› W¢6;fU›9»®X7jS8Nª‚c,r)4&FäfJȪµçò×àÞmtuåѪÊ0 ÊÂgqYªýùtHÅn¢,÷cù>7÷«¢l±"¡-]³¼O»¾PìºÝÇ]”—éÊù߇ЦxÃÓ ˜oí‚ô¾oÑ ÿIP*€2¨èþ6EÕÖ¹¼ÿ/Š>þ ³]ÜnlÔ—‡D=عËç…^‰¶´•‚÷½×çK8QáÞoùëæëýõùð«÷SÒt 2ÿW,µûnTl ñCÕKÊ„6fÁq…»4ðë1¹ÔÍX}{-¤øAævÝûvÛ´wvóÙ¼‘þŸÂìn7FxJm3Þù2h 5mÆ™OJ&{“éEŠIØŒÇ ¨²™©n6úV<ùíä>Õ ÈÜ$1…41ûŽÑÞfý?ýmt.·6gT¢k`ö±û¼€–ɘP± Þ9èKnþ1ý³wúëž«üõ¹ú«o“D…/½õ~6} lÕ`ãGº™ÈøÕHï¯@½+ÅñÎ ­u{.y˜]™çT*š0Æ<®k-…_ÿs³à+TÖÕÛÔèÖö¶Ú¦r,ô‘qCyç«n¨eµ°¼*@•C ReË\[¡¾¡ ºü\AfoC½÷îÂFÇ ÛøÝ Â›¯q!ckü4_ìô^XUðá/Ü%´ávntçUÝp‡¬›³y-dnÓ§<ÚüÇphöÖÑ“0¸žaKÁóãE<•§05ÖÿF(p?ϲHqY–­eŒ.g°è%@ro&’LË p³²$8‡/R|H²>r 3ÃK° –PôF }í†yû¡@°®¡í2¬‚Á&Èóß‹_üb{ç;ßÉ‚‘ðU@AˆÀÐÐs/¬øZáž{î±÷¾ó¿Ú—¿|»MbH‚älH‚ÿùÿ£ýä/þš-ײ]\]ksðø%X¨ uÀ#ø xÙM—Y¢3a;ÛÛÝ ¿³‰E ÛZr2ŒèçÞ­m6=7cu:º a1p=Ð;Vyîåù·X®µ´y¡»Ð€®±…È!±t( ÷ íTËe Põ ú£\²ã¹Š åùJ5óëzx”4Hæ±pÛï•ØqOZó–·þ|û _÷rcPΆc0öû9è0Zî‰ôn|#.‡·{cB‚’1¡ÂpðŸó¶_ò×c÷ùëöùë µ|ÀgyJ_¨¤»ü;¾n%»¿h‰r^¦œä —_Ry¥®X ?Ô>|eÌÁ“þüHO?@•µoÙiU8ÊjiBð¯C?}°sZì“Êp#l4€ tîigß‚‘§qÈ÷˜å†â—.ç®NØö«®°Ö­•vöÐIX°²š8¿ÕÚvbŸ¿ÿ‹lüÇAã8ƒ OôYýŽ7¶ 6òB˜¼tl¼­Õ­S‡Ïkˆ„ë?!?\ó-’r½ÎåÇëÅŠ sÊg¯L/-οÍÿž\åç~ìpž¾ñÔxÿ‘Øÿqìþw?¡&BÀê/O‚ãçà9ëNÃbhWЖ¨Íp ’` ŠƒlÞèWàá Uäÿšš6L ãzwŠ£›A!Ɉ}Dm°¾y ˆÀN§Ë/§@µµµÎ`è¯6{!¢hr蔿^Ðiýo>þyûÿò‡&?b ((ÿów~ÙÞóçiwï³D#šËØ••Áj®Úx¿qߣvíÞí@ðŒ¢h@åàÙÒi°x’eÈ#\½w« Žr2¿]ÜÄOð8@<_ñð®¹^ëGüÛ@¡AÒ©PV¼mê¹Ï ñ¼f”(섯ºê7€åäzù•2.ÙDïdß$Âqï:EiA°Y6rÈð ·A‘&môø+ÿ؃§Ì ㉜ÂþoûØ|‡Ùƒ'}ü7xcB&\9¤ºýÄPh†;_ÄD×ðÉJZXj×笤õ6é)6ß6ôï1Ú… ¯SƒÕ‹E¾'‘D—d¿´¢Ü©àu"ÔWß^N£ œ=á6õIÜsë$߀gL ¿é±Y†¤³Ä'ëœý Ñ]‡[ËÖMÌo½ÕRÔôl´¿ÇýðØï?ƒA¼ÿÖˆ;{ôËlüwØ©ƒÍ<‡Äzû¤ÙO†­+ 'ò¨4>Š•`³• #õ ‹³H@hÈ]£»èÿ[U¹0ÃUÎU u#0­i.K/Sq]}˜^\˜}ÇHßñ笺_xÐÂk àæ_J?‰70>tæ!€×WÕ¶Ü™L¥wl¬‰h j0²ÙkˆNa:¸–S}™\¸±1f TU–¡.·H ó– êšzwZŸÁFw)du± jêšh¡‚ ΩbuBLÈáG%¯(O_i˜Âßüæ7yßø ? ²  Óü¹‚(“X [„m¡Í?L(!C ï{ß_Û ×^g¯ý±×Û«^÷j»ýŽÛÝ"(AÃ#'û¬n3j`'+˜&m¼hÿô¨ÍKV€ dh§@—ãZøn„šæ±š¨à^“‹EûpvG™áòà §tˆÿíôß@IDAT®¹m¿HaŽ2GßÈeó­\: C$4î/Ë*Ó5–WjV!ÜJëg¼9×o¸™ C|Ū0¬THnY'1Ï:4c5œ$ü`Ú¶½°Ù±(¶Ù  ©þ_Íô§ Ò<„Mü¥¯È垈Nú¹+DÝáÖ—åC~÷Ûùé‹!¥7Ÿ¨Á!σ÷Ix+ °õ`¬›ýIÜ\W·ò=À„"'²wEM+Bx‹ÖŒ- ÅNaozx©|Œñ ÒÛ¶£“oˆLÀ‰¹aN÷Xó«Åò_j -¤ó•Ñ´¹ ^¿„a‡ìµï¨CÀ¯Ã=Õ |óð÷Ç;p/|½§3§Æl)’Ý#È'ÿ¦C<7šÈÒ•/˜çkùßPÏsñXýUeÊÜ5@ËÙ†âñ¢5c™ê‘*ŠuΗø_M¬(èýRaêëÿ‰P÷çBþóéz 8Ï_{r´÷qÔG~¼¶±óNŒmì>wóè b“=αq7Ð~R’²¤'µ®ÌÏØ$¢Ýålæ¥H —c l‚K™LÃO_”1!¹ïeƒE0PL„ ¼G V"<´0Wn=ü°£„>jãÖ¦^ß~£aDa ‚¢P㦙ˆûzÐÞõ®waó¿Ö~õWÕùÐIëáz,?¨&íjÛ¦]ŸŒG<–~ê·ÍíÍÖ;ò žMëÝÜ냟»ñ;‰ࢼ|žöË‚~UX|©Ë ±…¤zít¶òš yÍÆn« >o#ÞWå—ð}Sðú±é¾ÝF‘—WÉê†r[عÄ3ÏP+³q6(Åç%À–váwsíæ #»FËA5² h¸ºð½{×nãÙV¢¡›h:ƒ>øúxÃcðòW ›¹…7¾šÆzë?1òUˈ’%Ë9Tø6a«ŸÓ?§uŒ‰ÁÛg£4pæ„£Ô¤ª@ا%PÞ_ Ü0Ôƒ¨²R‡\vûËh[E5öüÛŽkP·EÕO¬žúö²D˜ã>ÓÍ`Äü,Sê'77ãß"Œ$½Û×5£²0Ê óUBÑ2·¹GµC<~ •u‡ñf3|¤h£¦°taVê²ÃÄr÷QŸÄêäÈ4?ûÛÜ>Z}¾]/!à‹ÏL @}í'«j[ï(I—î=ç-Üà “ÀOTQPßÁ›rEM š-Ï€ªX<ðˆ¾¿t†—Vd^Äc9)¨,%8aËdè"'~!s8×)¯ª´ÔR-š(:%–Ñ-øò¬]:E„ ù€¸uÀ¿ÖU§þù5X0”yáO~ò“vÕUWÙ—¾ô%Ç HŸ=§†1tõ÷°f<Ø´Ék ¤M…eŒÙ>ÙcW]¾Ý†1l’fõo&Zb“]ÑPæ*ëG&^‹‡zLRÏ¢À¬./,á[­ËÝOeñû¯‚-(,vSê([®Ëü¸[“òµý¯o:4„\Š-‰†_†ø›qºà³ÓeÖ¶«Íö£—º¨›A!:ßásÜX‹÷ÿ î 2Ÿïü=Ÿ×;úÙ’ÉIñB°U|}œraÂz‰9SÛRm³æaËca†ºØw<$2“òΔYu;ª¿ÈæˆW¿Â|>sà0:úØe(1ñRµf¬ê@¯Óß/«à{¢·9=&—»´‰¥Ð¾'ð+³Ömmvöp7ˆE šD“6ÜäÚËœû.°ž×F†Þk|Ħãeñ¸àâm(½¡ >„U›>e:ð¸|E°±*¡jÑ«›‹%u]2÷“ÁÉAkÖÂüÔÐìÔè`Þýï Zz^%/!ès#êx}eMóLxÎ:ç n´Æ€´a¢‚ ŒãTVa‡›¥¥šžs, òˆ!ó"ª„µÕ[mtì ¬,†áJx ²|)¦…çqE,³Ÿ³R/ªY@Òí‚ õë[w³ÔZß©Gœõ0M !AX/Ö™'EA‚“Hõ¿÷½ïµúúzûã?þcëìì´»¾û¯ö¶Ûm`òAÛÒÔΣ ³ÉÈÀÈŸ Ë689jéŒ TGô¦ôô“·²€â¯3ÄU)ŠëâÚÈ^ýæ¡$\Û¤Ã5V”Æ+‡{f ÃÕ‚XÔQß5«ë£üº#Ïj¤þB…8>bW¾ìz;p÷QkÜÚn—ÃZ®±£ß8m{^²Í†N.ÛÔå|Ûû dýsA¿çÝ«!~%¢ô¬¡I°ºÆÓ›£·œlèâï <õˆÕ4zúe¨à‚üÊ­®6ÿ¦M¨êõa ë{ePßå„bž.•-å9¨kØç¨j¨î´UÕC¹«J³ápÅ¢'c| Ø4r­ðòñò‡ÇP!å˜ï•"Ìhß‚Fg.<»ð¶²Ý ÊöûqÃ+2at(âáªFÊ—)ï ‡øF(š!O¹ê.D²›ÿFîz]×騻……¢J(§þ¹™‰Ïà8ëPŸ* ö<Ê%ëR¸@o`j¬ïfƒß´´0»ïœ·p #Ûñh’D•f¦ûá°¦@¢_ ô}“ þ–áÚd`þÕåÛ»ÖPß Y~œ…ªŽÍž“~•üW¾ ÜD×)Dƒªz¬î½žáfw7ØŸàœÏ€äädèCú;vÌ®¼òJ—7»XŠ‹á/Ù†Hn ÐÈüM™&hCéÂi=RÑsôù½÷z @"¬>M†Š…üWêê­ʥܼVóSk-.«+Ò‹]Ãm¸ºýð§ìl‘‹õó9þöŠGmdï‚=÷êv«¨O vÚZvÕóíaàk>pò´5^Qeý‡Fm´gÚ*<‘%[óÉF~éç}Í{î>w ßû®‡yà««a7û¡·ê‰VC>}9zÃÒ¨)ÝŠ=~$ùëörêÇ/ÇcÚ<ò4+ÖÐÜÆ‰ÿ ®Í[[mrxõ¼R$üy \z×´`«ƒC»¨vKȳŒõõ`mo¼%ò’hL Æ§vA8ýÏÏR¹v™4ÄFX„š°´8"Á©uY º:äx™Ï¥†•:;±ï°Éð¨ðÝG?ÆÜ8ñ#%÷®B:^k¾çPž«ùcaÓC3»ÉG-»ŽD…F·,z»8 HfÅë­1?AšWÐ’úÁÌäÈë&G{~ñÒæï_švKá¾€£é²Ê×0Ê¿š*-¿®ø­˜ñxÈcßC>‰ªjÈäRäF’x9SÉ鞆±ŒÐQ‚ }z´Çjj;pÍ;Õ CH×§+Y@†*lâDÑÀéÖW©Íã¢87Â&< fäÿùFôÌZà$pûí·[oo/FƒÎØ«_ýj;Éõ…•{ì§^ûŸlüÀ#Y!@Õ™D¥m[G“ÕV•ÛŒ{žL¯2·h„÷ä2üO<+o]¡@eÏrñ|’¿‡ÕoÎJhÖµ¾…nœ5ìªú ߘ¿¡¿k èÁúšWZë§+ó—è7P¢ºîâу J%`÷X›ËÔŒ5tTÚÉ{Æ­å*TÅ8¡–ÂJ`;~–°@O5|ãSf7|Ûløì¹[z+Zl ã5×+‹Ã]ȸ¾NIÃÕí9mc'xÿØ×ÀmîÌä"¦tÓløÚgP§ÃSeÏ9’þÍ × dqà¬,ÌŽƒhã¨ÊÚä°¾F»dÙÇÝœâÇíìÁ“N3 ‰ÿz{Ó£°Êf™ŸUÖÔ¾˜e¨u+xâì‡P5 ]ÿ6Ô;{°é±ÅNú"åý˜ÔÞOoaùÅ^ˆØvã6* q•…¸Š¬âÊ/V‡ܹC¬Õxƒk5”—z¡»Ä ¢xÈ WA›¡“”¡T<ˆÿ•ÿ96pêÃdËñÁ¥½KÀÓ00í­¬myCu]Ëm˜õ½eõ-c£yu¡Ïa ϰYg–Ù +[ؤK°òÖõƑ+\ZœbáA_J6)© J¥/@uò"ìGpœá@(‰Ö€¨ uµ­À±0sJÙzÙ-b¬ü ‹ááa§vøýïßɈ-ðÚ×¾ÎÚ~t¯%nF„ê,ÂM˜ –µX³3,nmŠÖÉfº]˃ω¿Ïiº$µÉñøQ ,‡Ì?;á2X™ä¸#ý—U5#Ü×ç<ÎÍ¡ªâP‚:aY%„æ±&O|%ÈP?ƒº` ;2:4ß²|îHºz§Évi£‘ÒêͶ0•Û%$(m±<è,J ðÖW½Ôzqúƒ Sã(bEH ƒã Æº ;:›0mñ ;¬7áúU˜V¾NÕ¹GÊQ|›¾¦Þrv½ñYªËJóóq°lÝx$ÞràbŽª‡ÛÕç<ßÞÈYT¿2xpcÓ8=m#G‘õØQkx\lÞÝ`ƒÇ`ÇtŸ @üÉÎG<þvÎG{çjCo,Y1j©MlÌ%ƒN?³ÄF;åOîÃXÕlÞÜÈ3(¸·:Å©€´?>8 U äz–—§À d¦÷8ÈC›ø2f'‡ÐÔùõÏ2vŒ…øR\!\}*äz¾{"7–´¿f¿Cœ¯"Ò+ÌŸ­ëEÑ£Öb`Ùªy‘ì]}nAÒgÊ/‰bE }>Eü׋ú2Àÿ/êØ±ù¯UA=¯Ã%àiüü0ö*kæßÌ\¸-]^õÒõo͘•ŸJKýÍL ZmÝ&¼z%YÜ!ã—”²¨`ð ï4§èiŒÁ—IXˆ…'@äÅETŽdX¤²O`‹•løóP$d±[\˜d¾,ah¤ÚfPAêØv ºÃHeåvÌõ»»Fi|ó qÿ§OŸ¶|ä#6Ò?h/¾ñEvÍu×:µB™#^`‘@ –àGRrÿòâ ëÖȰ¸"Ê‚L%h,@]rI XÄ~@©æeNß“#§0O</^è ² P‡p“œµcøå/¹Ó(!sÅFƒzíöoÁ\ÈJì…å¿Üú±‘®je­_;\¹…k†Ï×{uÁ] a|QÞoQeú»ƒÕ=4–sëY*W+B¢ö}¿B)Ì’y‹P‡pýÚ¸©ÙŽ ¥ŸYæggG5>0k;Ï‹ @®wOT5ÓÃlëOHg7í"]s°0@…Ú…ÀQ¾»‘û)XÈ–„ µE(l&ÊvËpš_#¤ü5úü4Ï÷ýt&“AàäDfàΔjP?ƒ_]Ù¸”½q¨åúò ýìû®ÙŽìs_¸NhÔ¤êx;ï±Ò+îdó?0$Ætšö ÷R¢„z)/tï“™e(dâ4•MbKÁ«åµlmc>0/˜/ËÈÌ”!0›D-W$|yá”MŽ©‘96økÝÑB=ɆßÌ‚˜Èìà¼gÁËD ³à¬ÍNÂç‡314`[/¿9Ø ƒ'ltøN;yðmtè3nóo$÷}Ná· éð*•yáÊrmæ`\žHû¡0\ã¾ò©?’–Õx¨®¡¼« @¸ædn|;p~”PW×ý%Íÿ.þþBO/è?óø tí?õLlþ¾ƒçoñUõâ|–‹ª× #³Ócÿד_¢ãŒòÂiºÆãÄ'U–Ùèû£ˆ€'ü|rY”[`é+C ÐigE >#΃H–B%XÎ`h¾¥,ÿ­P§äñ'g|(ÍÉDăšºfkhÙŠÚQó:?ÙšÐûØÇìÿðíMoz“í۷ϺººKâ¥7_Áséýĉÿ«-Ÿ#’½‚vëHA£%Åå†s‹S¨]PÉÝ~²е“Qo\[«¡E‚¯¾‹?FT‚ÐŽÏK–,YÛÔ;Ñ5O¤±þ׃¯´=¤Ú´ÝuR«Ûm<½W¡$>pþï©çÓs¦7°ôÞ¯XIû÷­®C9IkèìÀ(ϼ͠-Ó¹w'6J9Á·9ç<•uŒ„ïÊÊÙÈa—Iå/¿~¬O64¶-[€"6i• P¬P,Aß¿º¡JYó+ÁF> ¿²?&­g2ØèïpWQ;…ªíe|5äs°ËQQÖœ°3‡î±ÁÞ;lzæ£ÈÜÏ7ŠIö1ôño¾z4Š\Ûú éP BýÂüx¹‹nöÀú”ƒ¡%5šmH6žW)–@(ЇìpjøÒ‚LWæ7}šÊ°T%ë78ñ_7еï¢y,[DzŽ_‰—Â3õPÁÂßÏUZÓ' |ýˆÏ÷uºÍB‡ hsÈpr8îdÒx L@ÊÏ €\^Y0ݤ3ìÜ«E…e\Э/A; {`zzÆÊ¨³   Ó‹2„ÕÁrä Ô6B2Æ/AÿéÇlóÃ*HðO›¾ )>ƒÀà«^þbÛrÃ5VöäZ¿@„(å_™­~}Ñ›rt.O(C¨®ùwÐ s²¼ƒÐp]¿c ×\/\É,nG†ÜC;ñºŠÇ|\¶"FÎÂׯ˜LËö­6]?yÒòÉ!š«³z´ðû¬ =$ÞxÓùéŽÞ@²bÜRHóW4Á³±Bxõ½ti ¦w3lÒ=Œírøõ•ñµö;9¡ã]³3¾Í56‚®¾Œî,ÎÃFKÍ ¬ÚƒˆHôhï&©Ë à_¡¹ÌIîÀBC¿¢º£ƒ‘ûÿÍß|Ðö'ܽwÚï8l¥œöK·ß «‡Ó»Þú÷2wÃE¬ðÍ‚A–V”ás3$N÷¬~ÝÇŽÀûŸ†/Y~X&wÇ9Ñë?@dÿHòhÑ€HËFÿÜÌŠ:›Á3_ã&üt”t[çnÜ÷îîbº 8X‡¡ž„rgg0ÎÔÆœª°–MÛªý4B€÷ØèÀí´™ÛüõÐñ‘¡/¾¬®* å¡,¤)ʆPGñxÈå‰Umòn£Ï•º’°ù‡ì¼†ÌÐÈZ×N—ð YŽ&A¼œ<Êæ»Mÿ§–çnáÄÿ©K›ì%=Åè%à)¾ÀóQk #½ÇÞ1?;ùÙ¢3wÕM¢Ù¨I£¨›<JZÆxI7'gô¾QõË ì§É¥M}­€Îv–p0$o|ËœìEÕKâ–´œS ìŒM@$•ãä¥i\÷Ò¦XBät¨¡m¯;ɬêÒyÊ<@£££V ©ΩýüO½Ìe‡_Û}.®"ÿN´¨(Doˆ˜Ï¼þ)Äã¡;1í,òÖ~¦Ò§ÍvóHó쀞>ÕvÜJêN!¿2mXŠéÅ¢å´è®6¥A°r‰m‹¡Èý ¯ËÈ·T`lgl@®´‡ý<,û•”,Ø–Ë/³1\öÎÂÛ¯mª±³‡N;³Ù¥•Œ÷Ô"T€y6ó2l¤là4ìêJîší„ëENþXB€µ´¢ÙÚ·ï…Å0¢PaCÝw95Ú••^¨r¹ï§˜žAñòC^®FÈÉ]‹Õ ¥*+¬[˜°þä¯ ÔÒ‰_€áªDhlÍBK®¢Š2¸†hÈa­á-S…Æçq`iÿ+lúïÁ‡Éc}§éÿR¸@oà’àz±O¡ÙtSÇž¿+«¨y»›1O¦!&šþ54os’þ²až‚ iüÍNc&´EÐ"¶¸¸Œ¡rÈ“óÀUCògAC` x)f©Š´¹„=~ÉÌÌN ?€©Ò‘^ëï:„<Á…CÌå4èö÷ÿ–Ýòæ·Ø 7þ‚ Nh«K ÏQaíRna\yþ/Qna‹’ò£E+‹Âæ@V—Du³á¾\…¸)%CXïV&*-ýkx«Í£ý±Rj—m… €;'¢§–7À±xñ½žÆpÉ–VZ7ÆxgjÀÖàt_Q[O^{Öø0]Û\ÁøE[©t™dª’lüðëv@~ï¶mWí´Þݰ¦PÛkd£O¢ê:íd`š6mBB]}ìí—”N#×½3޶€Ymyè,¯j䊌Mu ä`ÚÙìŸ!˜Ÿƒ°rˆwÜ ËY?}Ý ßÛ¢ð¹Ã× éÂ/Ê ó•εX¬tyn£§•øUÕÖ»éªfëÉ:uý¸. eqoêŸùPïO¦Ó‡úN>záN«žã©gh-½Ã% À³ï«-÷ýµ¦ö=ƒe•5¿Ë*òÄØ4Ú5ZêG°:V…ô~%ÖÿæÑG®¯ãe@M&4ñ¹ê_­ýyø•² ‹d%¥8âÔ/·Â¥O-ÁÿL£KÎJI«¸†ÊÄFyem³uî¸ÖzN>  >óù²xÿþ.€­øÈþswÒÄóÏìóüD ÓQˆP.?¼ŸãSå]|™ßˆV…µ&z¸a^}%Šõ/sõXU+úŒEêÐÐnhËuÎi{,ϲvrº5$Ü{tÃO[f^ëi 2§tq=‘ìò—4²šÖ6s6iNöõm[‘Ì—ã„X1bUV@[†ñ9[ o>…°v/ð€X’FöE–/3ØìŸk•m¨ X÷Óp^ÄCßôÈê. í-´a6Øu”ú R"•…lqg=˜î»¬¾mò£Ö¾ór<»Ÿ¹S¥¬Iþï9ᾕŒö±hÔeÇJÝô_)÷Ù ?øªá Åáãß-°ë^5ß²§ý¨ewC~ÜÕƒ¬ýRœ g ô9Ù_ÿø\6wUd·ó ÚyßÀÙ9þÈ*°Kâ \B.Ä[}êmÎ÷}gCÛŽ•ŠªÆÿÖ“ 5ë&$32š«ÓÓCNg¹®®“EjÖmÞér”æ9Q±H.@') L±ë;~¿ì¤± 0‡w² 4 ‘µ´‚esóœ€`XNU7Û¼ç&È£Á[ŹûybëU–ÄR|óÒª£ žøÇuÉì/÷K¯2T¨§:ÑKÊ–e+g#Z¿ìa-+v4Ígk*ÚgƳíùò°_8ˆp} mù­%×ïо®>® ÑR³ÀQ Å;𲕠Ÿ#ü{8µÿl ê›0Þ’Í,UÛmõHì—Õ2¾°ÁDýÄTéä"{ ò|™L^c¬*]êUëZZ¬ÿÔcHäïä”> B„ T«d…ŒþðæØ{Nœ¶Ô÷l¥ÒÚ¶a°M‰y$ü3hNliZ²®;C½*6ýjNùK65ŒÝŒJù`$,•á–÷ãý1$ý¡ª¤ÇP!¢«ïãQ½oÖ{çþËù/(èPKuB¼Æ·ºÁß°ù ÜøÃ5ºCü&k6€@äb!'wUYxÝÎýf7åäªØšÍ)PVt÷¹Bcú,Yð`.îS!*² ;ŒoÔŠ:rËmu`ÿ¤šlþ·(+ØLÙKŸu,·é×ZIÓQKTœáQaYp–W6@ÂG…“øÄДµnE‚)ý9Ʊ(ÿã¥0vÕ¹w;§ö1øü=È´pÅT6¶÷'!ç×¶`Á¯®ÜzŽcÇ´/cYüþ’”Lôsú‡¯ßÜß¾ ª r&𩪃·‹ Â2æÎò"†‚`ɆÖâÂAä ~À‰ÿýÊûX±/Dߢ ¯¾^áU !/?çeãðáÎ…M’¯FV!ŸŸý m&]z XÝÄÿ× òó~¨ÿˆ`ßù?1dûýôF.VÀ%àé'Oæn©Æö]¿[^Yÿn&-ËÏ“ ~ɨ€P í³7Áò¶WšI¿Œ³5‚€÷Óœ˜–П¤ Ñ+`¾²qൄ•oMuŠ(9XZ€$ !û)lÌÍ ¡}À&0²r>ƒÜ_}Íõ64‰ƒ\mù~£ñ%2ÄÊ`»º½„¹¶s;raq~:Ûœ"áÞ1Ë ‹ch×Bh&Û´*¸¯áãKs7á`³°0F–£ÑQ‰[YHáXI”\ÐÚ-xŒxæƒz¬¿Ô¦.KVÄÀV §3Øåo¶ÉÑ´QXu@ùa/¿®ÙlT,ÎÍÀGžE‚ŒÅ j¬:©×µÔ’–°*Ü6ñÎÝ{ìÄÃG­e›LùÎ:uØ9ÌõvîÚDû#lîÁ@uÿð–*ÇÕL%›<MÅ–1ç[×*AÃ3 ¾Hñ/bNä«ðéMû¬Ê!”‡/§|ÿÅ<„â!¬Ê‹^ÝÉ>ÖŠÒ…AäÿìØ),,–.h£ ¯±ÞH5Wãï½ KÞÑßµï Å;õ4Æ×{þ§±OøV—€'üÊž‘ ©†Öíÿ á£?ðØ6‚ÙÈ›;vCBM³™³x"ñ¿„€_EE=‚Kxê“>Œ„”I‹[K J .%›ËœòË+›œÙጎ_G-AcN$ N~$Ð}ì~ °pŠ;OAÒÃMÍVV·ÕÉ/øf ³Ât¸ùZù¡|½knÅÛÐÏÓhHèþ !íS«v t5’}Ío« EË 7ÃÉét,©¥YFÖ1õ\‹ÇÈ.QJø'à™T¯Kªf­éÚ›?€Ô¼R¼P–—ƒ@žæ„Î& Bj‰xî­èß7BÖ?u‰±HåJlë'Ånÿ´Lï6C1Aí•1Ùºå2Æ*¼üîcŒó+m¨w?*Zðõ»ž€šÀ‰ìÇɼ@ ©Ælïpo7È@ì„NÚG~`ö¬cLMýˆÃaÚîãnBŠ Ãêo â_,ž⺆öâ°þ{Ç¡6¾ÛäµRؽÂtѦc=ˆEã .;÷/âÖp.:I|ŸØ_!Ñÿ¯}§¹0‚BùwFRZž‘ž­ÓKÀúïçÙTšlhÛùËåµD8ïÉ!Ž\‰j +`5쀊 ôü!‡jÅ”s‰ÊMïÌD?ÂMµœîÙ‚8q—à0 jÁRЋœ¶*¡$LO „UÓ‰ˆ<™žB—ºáÀ%ôÎGúàØäÔy{•°!êÛq|¤Õ}Uˆ¯jŠ+hu*ŒÇóŒ®!^Âr¡žòÝ*Çbæ¯ÊY7¬‹g¨Ýx:JÆn¿M€Î¿ª~ÈQOâq}Û[mjžt¿+‹ìvHÅ'K cO ô 2àµ$øô†—¥Û »~‚ñ±ds‰Í¿A²¥8Ø µ°àÈõ²Ü7=¶ÄF¤àû7àì' ÿ4¿E?YI±Eñ9¡d8Uš„jߟ“þ ‹eº´òꨕ# …h ü×ƇzAñ=ÐϹœ<©b-‡=e]PþÎCÐ"ݾõvøÁç ñÅ)¬‚«eVmÆEÛ˜ ñ¹\Þú”Y¼/‚P“¡w.…õ”«Òñ«/)ü]^¸Ñ–ç[i›í6ÃæŸÁ…-š’ X™©¼ ÀMP0]÷4=Ÿ6þÔ¶ƒ–¬:Ä[…ý6Ö¤3­[ß^m=Gº ab×QµCÛ£ª–>σx¦d´J<ù&öxþ ílê#ŽD?ÜÛc[ö^Ëø©ÀToÓÖíèáwA¦G R'{!2-ÄTãR<‚¦ŽÍXþëB¿Ê!¼UõuÈœ„ÍuU¾È»x ƒ{EqöïÞSeн¸jí/ž &|ÿÿÝÞ™i–…ý}gßwÏô\»³³§Œ…$À0–±˜Á„Á˜æØãG€ ´B!/’è¶Äê!$A`I ÉhW«=´wÏÙ×ÝÓw÷wø—U•ïÕ{ßû¾>æê£Þn¿ªÊÊʪÊ÷MeVUVVöËj~^M1,µì/xÚ´¹mÞ®¬æf’6?Ó¨v™æWеnóØ’û•b©üå©ÉÇsµ’ƒà~OY">DOûƼo`üÍücßÁVnvHa7ŽpÿäÜÀg¿Ìš{ŸMfüMÒ2pØë[™×6å*Ó ÄŽU¸ Á¹©Æ&Ûn`<È?zfmœ»îŠ60¬±2°²4Çlï¥6` d€l_dÜ“XÌÉâùi?n ¦^Éò»1•L¨æ)R¥“ǯÞ$r3,®¾)£$´xºu Õ¦14§¶þÍlƒßÇJLƒýl:qßCµŠ1`Ô˵³ýÑêl7濉θ¹%¡´®÷4¢=¹¿¬$õcX'Î¥šõ^”È)ãLjm 'S›\Q,ûï«ü¶èj ÃÅñCÁŠÓ Æyó33FHs,å’c'E~Wó ¶zÌì] WE)•«xK(5–üËxð›;w-:rê(GÿØÞZŧÿ«T×8¿ÏvC‰#€sçfù=/"ô¿ŸÄkßGù4 øÖ´ƒƒ&_(ÿ‹dhxIÁ—'ïëÚœ6oæ¿+^!m¸òk5mËû̓ä =ŒùšbØ÷æÍÍÕçðj*晇êñx±¯ú½»¥ä}ÕÅרæÂÜÙw ¼×0 |+ÇhÚ¬åf‡#7Ìè F¶ðù+“(²ç?ˆ•óZ´Z¿ŽÀoÌœ°ú¯°BPgð6®ƒôU.å)aÀ0 >÷›bK  ¡N0¨V8#½À$m¡ÃRj]ܩ޳}ÐËEB3Àƒ¿ËGܱÚåy!Ð:dZˆ¼µß ¾—ôÖjh§@mÊ)y s¿½Š„ŠØ/êcY2ÚŸ<¢>¶R‰vÇÍ¡°üuñø¸¶Äl˜åËëx®cI]Ž¬Ý¢§ïdÜ;âÇ8ž·ÈŒ¿Ê¾üiÀµèâ‹OGƒ#'XE*cy7ý/ái«ûå+œ¿_ íÅèäýw/}\%·ì‰QŸ\Ó;rŒk©±ñ›Ÿ¡׸›‚ßå‘»r<ðE£( ¥_¬Ì_»zèÞý.š£~k8P\‰&Ÿœa%¡Šãž£\ÊÃiƒÒ$§JÇSßSÑù畟ðÒ¼„§1«%$Á–GC›Êû¥ÚÅÓ/Ü4DÉ÷öe-¨is5Tx*ÔZ´v2ÛàÌ<ÁŸ†]†Â;ƒÞ^,–ÎN½ü˜Vª5$ö.ÂÀÞý6[µ¬04~ú{ûGÞÉv€Hä]?âã¿·c«f]¬ÃÊàZgД³ÿâ1°lfÝì8zÅŒ_ld)Vîå4'̹]m•ËSJ¥[5Q˜…±eÀ쳉G¶JÞ9x£Çe ,±oÒ ÷¶ô’ñl[~‚žCQ2ÝhëE”*£u$˜*¢´µû„Ç‘(t WmˆoŬycAÊ!’µc|×°p,S~÷IY)껇ãq¥Ç£ÊÀfÚœ,Á‰Ô.pÇNÜÍÊÐ"¿¡~¶„˜(ð.‰ nâ~úèÝ÷ pí.¿Å+¬°Ì$ÎwäDIS ã§øB%IŽ­^½tA¿ŠÚ‡+^ £%î·è>Æo”¥}~oÃGï5Kýó³WhÑ2ÊÇQÔynù»'ºÄ¥?bË‚k„hnúwÁ{Ñ-óK¿})h—ò²'ïSæ•ÍÃk¡Ÿ'è}$æ¯? ¿é‚›M›òŠìˆe’Û—𮀠¼g©æÿû¦Ï>1•B:¤ 7ûŠa`_}®Tc› —Ï>г5–8ßËe=mVRer„>û£óÓ ¶¥hH–^™-ŠÑßÅGÀæ&‚älV‚e,µ7gY1è7+%Ž Ö€ËlȲy¹„o¬±‹ØTd뀙èæ&30”‰»_…uöÓ,çÎå´ckÐöîÈ=âI¾´·õIòu4ÍÃj)×I‡V)‘F29¼b ª6ãoŒ&m‘'ØdîÛâDøÖj²G^aõ©JV³,«c””X7g@Ì1K'.Då¡—¢r|ÿ¬ •J§`«I‹ÜLÙ`}‰Ã¦=ÝÑ×/ÌM±¼ß‹½È9ŒD/0«?ŽòÉop…U#ŒùV—ñ[€Sq\týŠû‹F'N×¢±X]Z7Kû¢È–Àê"n9؃òZ#Œnü,]ƒB…ãM®´Þ¸ f˜íÿ¿èÊå§-;•µY^÷ål6[ÓÂm%‘÷u&8Wc6-tˆ¤2OJ€‚"›—ɧŒV¤ÅSéT¢WËHÈÏnvù@ —ºÉ–’xV°Ó”åJbq "× ‹Uú±3ßÌÀÿžÔ^Ü1çå®û­ŸÌ ×R`»ùi<{‘I–"mæ„„àKFæq ð2‡Pf²*!OR“Æ,ÜÏIh+–º{†6™Q#ïEα~—u9Xá»5Y,’‹Öv÷H+Ä`´ræëQ©!ÜÃï¢ÛÜ–706 ïâó/²ßOT<‰°çbÒ›(™k«×X (0³A¸_AA)ECÃÃ;ËjS ¥øãç*¬÷¯MÍñ›©°JpcÀkƘON••¹±oôØeÙ¨ ¢è̱Ô/·YÊEV¸´F‘èá éJÏZtmîyü<_ÏR¿l'Ðx£aIè÷_ AèPA1E\¨%[C ž<>¾ä'8IÌ jž/èýe~E7?‡¬•UÍWb[…mð-;4SCKÌÍjÑ¢",y›ÿ‹š9wðÎðoźƒœ€ðu.Ÿÿ8£ßrOßÐGÐη§è@÷ì–Ñ5–ñePï–Y¾u”€ûù›b\Å suÁ™iV¸G)—5”™EÔ¸q½Šm«¼QÇCE¹PˆéâÚʆ_G¢£'_aV æ.0ÛÁ“?c߃ªCôVåâÑ–N¨ âí–UÚJCVWV£‚H>ƒÁ2c¯]~ÖÒ6tårG|S(…Þlô3û•‹p° gufKú¨ÁªŒÌúël¢9K²·ÓG–ù+Çàý/DÅîËÑØÉSöaÃEöì±FÇ<«]EX`p%à ³õó\¬ÓõqJdîâYŽ—ŽDS/¾„QÞ5¼ñà‰§hkÕMíÆi‘¬\záÆ{wq‚dÞàzº¯”Ý%Ôi/ÊÃã±°ŸŸÆûßqóû«`{²8ÿ/ó{å÷Uû4Ç'ÇcmºŘ}QòE³xæû˜¯dúEîÿ.S”=án)æT }‡aÊ(0“墨@sóCEâœÜuÜæé´díòäÈÅç(þf.YúìÕéìþ6,:à`p€>óàèÉÐ;0öŽÊîª[²*À#Cßàð)ãÞ¨_›Xÿ—8"(³~qŸ*³áR¥‚[a–nÙ.¨×YnåáB™àܪlpñJ“YWµˆã +pj›K ö}œ4àl5'®slpêÅ/S£Z¦ú¶¯±ã²¼Ëž¶ŒÛ"vÌÐá»#Rœéî¨Â$Æp‘¶]ÈÉeuLEÍRQc[›C£ ê›'øðH¾¥²/ßEîwˆƒQs•=ù•oÇàqÝí"R·ü÷<‰à?ÙŸÇ´ŒUý {ëá¾A±YŒæ§P NœæK61þ›Žœ<#§íp£‹5þ…Ëà EÓ/_À@ïÞèâsÏC™ëÑGVæ—±ô?…‰jŒPW¸ðG¶ŸºújØ hV†8M2ÏwïÇ–ä(mXE‘\§ Eöü±ì_ÃéÔ ý]âvÊçù­~žþÏÐ_xf™—é^š—Êo) óÓZÚ'íÇ…N6íÓ)ÌËW¶EP[F ^ԯπ“—ŸE™ø—Î?ÖægÈ|áçgÏ?½sm1MùP¤:)T{™a`/¶íúÕ‹Ÿfäø>ÎäbWJ€‘Bv%`ág¤Wú£Ò±Óx—cvÏQ*q´Rçx_¥ !·£U¸#€ûÒ‹\5,ƒjõsu0Ææ—ÕÕù¨†}b3¼Õ5vÜ×Ù/„²¥0€·Âýß‚ö¯0Öl½¼ŸØÈ(–7*ú0?î32[VÓù81á©ÓÃSzÙtÊKÅ”Rd˼VtþRÞîËze ÅlÚ«¦ÈDMÎÿ~²ô_šBç{¡pN£"çÛeo ý#ÔË#«{AZ¸Â»9hp·@t:«|ï:+9#Ѭœ0˜b`˜ßĘ1èëîïã—Ê—À㋯pì§=ÓWαßÏvK÷Ò¯á##ÆÝï‘Ów±]Á—nà$è Î~.=Å gû7 ýKä³ÅÄ>ÿ2N~67ÄðãSh_|îªq´påOØrâü>‚ß0R™jºèHpHJ_åñ?af1ìÛç¼W-›—§8éPk(¥LÛ]›•HNZiñI¥ ÛŽpû¿Ü¸…àoüOÆŽ¯roçI†~HîO„€ýùÝ:¶ºxâ;û†&ÅìxGļL™9ºÁBfúýCG£®.vU£. ý–WXZÇÌ*7ØóÆÛnSÅÁ Ëÿrt° ¡/ûµÆµpÁçAUöx7Í @5>Pkøt_goxEazÏpru]‡'H,/‹!Û­züTL«IÑvõÚ£Åbt43ÒÐÓfHJÊØ·ÍSXÓ Å¯ã,ûWYi3No Eœà`œ'—ë4›|œ•®¿Ž€×Æ%4"F}ÕÓ碓Ù¯çÞJ‘Õš–ö²«Ä7CÑ“tÛ n…”¨r@DÎÔo¬ÖYÒç·Ò_Æ[†õ«o–8«Ï1¿ÅûIö“Xð?H;8!ÒäÚÜf~‹f¹éêûõ ¬ˆŸ8’ÚˆŽž>ÃJÃ>þF/}õËÐìâ8à —öL“ÓsxŸüKt¹‚WŸ o¼ß³ÏÉtÜòÞr;ù~º]\k•üLÍš•½mSÂ'¬˜JLC…·„®Æç ~)à~×{,Qó£ü½…Ùþ“-UÀ¶8Ïçm½£HaàŽ²ÿÖTŽG>öîŠÿ´èÈÇÑæ9™½ƒÇr2L,²L_ë]Fªw1ä6"\¨ÖeOœ}ÿ^®Þd?y¥ Âê@/˼ëë–n»{Ì@.¾Š ÜU ¾6¸Lh %@¶ä|w zföÈÙ¬Òý½Æ8p©ÃEBÖÀú²cTµ MÐa”UôŠ&”¦¦j‰Š\j}òmI-Ði-/~¸ê™Sb_ÁJ~s•ñ—¹Æˆ[\³›ñ‰¢%TK|‹êýO¡Ó½Lù!¾''Ø:a?4ÞUû æ’žåù•häøp´0³`lC&NA—%øYYr¯ À¯Ð·‘èÞ×¼*zîKOp•oß×ë²}Ôà–½c÷¾ŠYü¬Y⯭c0,JJþe.Ü9¹ÿ5Vf£c÷Ýϱ¿Yà\>…Ñßê"Ž‹üžº£™s_@ix…ó%){Ü[ðÅû= ªá½aœå£¼…‚üeウ0‹Ô&yZFñ$L=*ì5ÔL?íïñ+A­LCS.•° Sz™0ér¦Œéå­©ðN~‡àyrj2œáϰñP$à ÀþÌ#'^Ëå<g%`gJ€òD¤”M¸ ãªÁañ1¿õõ±ÜËì½Ì>l³ÆÕAùËü›l #d?—Y¾Ìô†&Œ‚°¾ŽkaöïëÀäÊàšOƒšÜ8hNª¡TȶÀìÙ'96† {Î3Ä^qÿðÎ5rHuÉ )#±ŠQ(+Ý'Q&£ã÷b¶—¿ÍÇP¾Êd_ ”C&ý—X’/Q»Ô-˜ ÷¹¨P“Í+K]à>¾_£–Ƀ%F|¶fƒ+Š€<~%Òáí!û q% ¨Óï’ÊœàžŠß†-Ç»úh|á¹™د @ظ™¿‚=HëúÕ O³üû{¢áß+W{Î쪉n„[YÁX‹+‡™)Êe@œ³È…@|Ô,+3•—Y[‹Yä®"Á‘­è5VX f†ÙƒA)~,œäÞ§ 8[Ž@(²l}ìžo‰®Í¾]¹ô\ª¹"¼nîã Î-„m§ããH1Þ‰.å½lSœ—€ÜJÒŠ~Q% ‹%ivø]3ÿ¡+`9Ï5ÏuN]4›ëüAÓüBt„%õ5³¬/½é<ÂQ>l;ð”×?ÊÀâ:«0“(r\»Ë…:KXêWºÄB€ÿ^êøzÄhouÇPÜ,¹ºt™Õh—P øær#ߨÉ{pÛ+†€(…›×©‹ë‡Žq¨ïÒ:çöY‚š¬-^¹„A þþñAÑÕ=Â…>ÏEÝËѹçþ/Ký2ã—Ó%pF˜wY#’—d¦ãH?å«â$i)§ÐäXN&p‹eß~ oƂݣ,Kûfæ–†~á¶•8$Í—JÚx ö‰º9|r`só1ðxø1Îð‹¡_xb fÅÁàSýkŒ·§¿ð‡œï`Ç= {'Àuöé‹Ü$(ôÈØÖÛ;n,ÀåÒ îÀP€™ûÍ ?]"Ø.ë|kŽ&Çk›Ü N†0+ÊBÁÞ(nˆÅ¡Ð?~?JD…#dâÈEĆ6uÞ2xzƒ¶¡¨0 yDþÄ[µšms“â]Á¦`L»%3A#¦-°XZAkU,í —  Ì#½"Û3¥ê«Wà;N›šrù'1pÎ4|änöâ‘\ÊÓÕ/·+.E½Ü†×ÀÿÃÊ‚8sŠŒÐ͵ÏîˆèÀ°ïå)öì¹Lxl”Ë–ðÊ'F…(\·+ñ¹»ÕníÆåïÌjtá¹£üóóë MkÑñWŸdFÎ\Ç;0z„ø¤1ìä·ƒòˆ‘âÌÅOa\øl4?´í¿áMÌ éŸ| 鮯5S05.ù[?‚-¥L„íJÊFˆ+f†¶ùqø0()ª”k÷äf™Úl‰Ü|—ÿ›ô‰›Ù¾Ä‚ÿKü|#Ç!?3wñ~á hå@ØhåÉ…ôöÞß?râcl|ãî;iG¶.lÆŽF¸pÔ‹5î¨#ÜË82>ÛqÚ²ÎìSÎlwáL¨Ž€Ù䈠Œ‰–6Yî—£¸«][æ2˜¾as{ Ð^㸠ý]¿v»€¯Ðܦ™©Ž=íš®#ìî{Ò¾dzä5)p5˯^am v@pYˆl)dA.ÃH·«À$»Qç§:¼)‚“}GŒe}ƒ•q™;Ð÷Z<=Þ žÌüÇá÷¼YößXÙäšÝ#,óϰçŒïÈ¢DÌsMîÚ2÷<  uãù¯oº½}FПzhÂï-ÏÏcá/þ¸¨¿Açû÷ƒæ˜ÞÔ‹øìç&@q.µº,žûX•à„@¡Pgua*º:÷'Ðþ:¿ƒëNnÒŸ¸KôÝý­x`y#XR4ýØ•“V¸ÅRÊq•éÂ[§båÀQ2„”š 5™¢¦5; ßøL–`þ}:À:Zwó³l·½ÿXê'žÛÁýº€ÛñëØCu°Ü{šÓ¿ÏvÀ+wÝ,7 ÿÿGOÜÏ ¾ 2«!.þçåœ?/y–ŽYFÞÀ8pme!9ιqŽ—á ¨€û`©›lX‹}€,O\D$ÇeaC4–ðlw1š›za$ÇÀŒ¦·êñG^;jÛWêóò$ê7Ã˲-S€"iÚå’L¾µþO0$f{í¶Ø–ïžk›âî·‚À•ãz²z"ô8ŽY‹Žù›8Þy%J.œKt®ïkb$¸±vVs䓵wCl4ØŠ¹;š™œd¹]rª£Šg?q©Ëµ”·2vW¢c§ïAÑ‹X XdÿˆßA!ÏÌdš[ Åtµ»ˆ-À*³þ~¶ PžÅUïŸS×ã(z i¢í·m¯å½Ë 'ü(ã˜xþK(oõhŠ—M+¼%T@gû¦2JkZ ä6À%èP™ívÇr¸à¡ù þ=ŒçÎÇ™ñ»%³––À-â@Pnc·CÖìn1à œêÿX¥»·õ0¸òH%Mç†xÄÈo ¡\f;`U€‰h÷°bøÕËý­VÖ¹ÚµÄ @ƒigeüÂëŒÉã\:ËÈ×®qôL–®#lê8 BK@‡@ü±:°É½‹l;\¿r1>v&vÔÌmØMÚA7‘K™AX“moe¢"zÍÊd¥1üL!.OÃÂZ߸ØA>¿‚#—/Ã[«Xu1Ó__˜Çbÿ¡hìÈwqz£×\à4‚'½%fîkò ÌÕ«ÑÄ里Qà•©+Æf£ÑX6ù…BÕÌâ9bJKjÑâ§:†Ä!x¬¢$pÂËwfΞÇÈï.ûQ^&¯ÁÉ1hb–°qWÁO ”<†Q ËüF’®ùý•>‰ðT¸¤õñáù¢µ¤¶ã¤©ÒGÔ¸ xMÇßBÚã¨)ѶD´°¶,SN³½°“Pqy\÷}¾‰í²g§&×VxTBôvp Ó·ºõï¶Ž°°[Îíór=ý#G9R÷‰JWÏ·ÝXWØÏG˜fi§@ ôbé¿p}ÚxìâXñ Û f–r]|áqcÇqeú<§ ®ãK`2º:óe¶Ä“Ÿ<´=n¾ëã¶fùºœ¯¡£•Ã'妩.óêÄÕ ªMª`Biã5Ì”jkk\Ím` í3].£÷PÅÛfÎ?9I¼ML›Bò–q (·Œµ[+[ó(§/ƒ#'>ÂJÀw/#ª×ò·A èã²qÓºÎj@Äé€.‚ó ÷2éÕÕe .iñ' —V™:®ãVX.A³A^ž7™ùï‚b˜ÆƒUœ‰o VÄzüæ=yV˜WÍ;‘Ò­U+ºäØ"~„¸' ×áÙ%~]îþèÑðQ²ñ‚x0±³ôœø¢í2ûü[ï;‰ð—U˜hyžmNlôöO`y߃_þëà_ŘW¿Üêxü¾“œÄ¸dVo°Ö„Æ3ùe`¬à*jš½lp‹_µ«—í ±å`GîˆààÔ䟲‚óØ".ˆÝ#¼ô…}×>j~œ§…óCå””Ì>’çÃ7‹×6Ïò¡Ç¶Î%ê×L\“\óÓŠéd2ÝÊýü.0Ö=BÍïž9÷µ)iÁ~<Ž{!¸Ã§îð¸“Õ¯¯,̬Vº¿Ÿ6üïJWïß‹GIfÛâ©o§ÏÂÕ üëÑØÑ“läâØ·"ÔqŒÙ»•ÜßÎq¿nöÅ­Ê…C5N…K‚–®Ïrt°Êl”]ÝDZ»˜ŽÎ}ý+Øvœ ~™•Qx/~ÿ¹å‘Û«]#|;èqÛàâÕY¶8ÞÙ[ˆæÎ_Ž®Ì<ájtyêÏlû´+®s’4=á.O&?ØìvtŒšÍ“ ­&FêIÍø¡n?Äâ_•€,Ñl# }W«V®¡«;à.¼‡ß`ùÿþÞZ,–?ˆaßœ‡¢7Ä Üûö᥅™Ë««‹?42~׫ÝýßcFÒ]á^¢…ùiÞ<Ž3rq¶ßœ ¸õà÷ Æ€Ïó.sº@{èø¦ÂÞõÉ´ÚW¬ÌL?ÓIºây+ºn£dŠûp‰oÁ9[Z¿9ÚG) cÚŽj1¿?‚¯ ˆË¦™±ðóm—Ä”x#W|lzòq–ÔÂ8ps9ln.?÷-5<c𾮞Áœ’¥»ê — œ`É‘Y=çÍÙû_fö_߀½ÌWXªÆ¬o€x‹³ÖââN„N…Ó2ÓÚGN 0"¬âDhvú KÛzd •‘z'Oë¨m ÙÁ]H*ùÖ".3§^WFÈ%2D ä‰4ÍË¡•j6qãË>|Sœ¡€‰0éí}utâžï@x „­p£Ž06ö Vd¯ÿÊÔyÚƒë_Žþ-/¬E'î»%ŸÿØmŒžåÈKü=¢|5öרJ8†b}®ç½:ó||™ºDF¹^.$íó.•N8Ó•pBJÈ£q -ÔÂ5¾­P¾ û3{I«ðßv3¥5RÎÙ·^´”ïd¡²·ÉÙoÄKæ§ðŠéíd)R•ÏÛÖì èÈ tdÏáÊÄ0p¨hâíXçÿ`<Ò ²£ì6ÙÒÅ2ÿÈØ)„üwp毀Ìí9&v¹PBGÑàÇÐð"¸º‚m€¡+Û=цƒ]û«³B°¶:GXGôˆ¿€«fÿÚ6A¦á6Ö‚–-o[bß ë ›‡Ö:–Kþü »ÐÏŽA ”ÆI¼Ó£ôZq\\ǰç?yÄÓ⑉ØY°õb|ìËé€%4õFÃãœÉ‡××çPŽ8·¼#£øXÄ!P7e"VVPîcyÒxí[]^Œæ.}¸\Ês^jqM&LÍä³}²íö¡¦¼Tõ‘tö|y¤´¯P( %[q%.O[Nù‚ß`RR‘54pïÕ×Ú\†&½"1HC^ž²¾c‘ü?¢2ãÿ<3~Q¶|‚°%‹B siÖÀ؉ßÞÕ=ðzÄØ"²ƒ¢ï ŸfnIš‘N¼ûqá3yKÍ]äq€œà¼ùÊ"ÃAÙ>èÂPî¡7Ëþä—*½ž§¸˜&k(#¨Ô—÷øy:+n6϶٧bJÈš¹ú.®¤M2•ÈANʦ­U}$¡ÅÒ6eÃ|²y,˜#€ø¨r/ÃØÑ×>Ê…N¾ÇÚÒËÿU¢#Tºå¦>ðQ ¥~|üs'6¥²8Gè?½ÀHÔ7sö)”±§Y­yžït!i€òG!F¢¹öÆ ÝÈÐ- ɵöËOK<áŒæøa§Ü,Ťœ–Ò2ÛO°¶Ž Ì“2°œ SÔüÑÿÁEó/âëkÓ/ÕßãÊTÒš @+Odû¸~û•Ì}ÅEÎzÿX„gØ®žþ‰àÛÅé†`7 ‹{ßUnÿ½‹kq8ƒ³˜NdP02œÀ¨OÃÔ‘F%f«òˆýÀGþ¢7²…PFð×±ج]3¸›5®¸çzR‘üµ{ü<Ë ì—Ó´¥áÙ‚%—™iZ-©Ð/@;ÆŒÌCIK{,™$´}TâÙ0Ÿ|¡ˆ g jr°p=ê¼ʽ4Ÿ±üÇàÂ,Ûo¬VáðgyÍÚ^àËÿ(Ç7×6Œb&}¦^z–ïÁ÷™èÒSïCYX‹V–'m³ c¤MD¤úh33i‡ Ô.uk¶%æ§,VEÁDZñlU F&Ïö³í”¶CE ûtÉ_‹ zK%À‹j Ø(A>ÔÆ½¥~s†Ÿô›PrŸ ·òµ2+@n9 À-gñ¾­ gìø¿† þ·Æqÿ®»aGÑ æÇ&d¶‰¯y\Â6±$/1-Ã謂3!ñ GÌäVº— Õ¸‘®ÜÃrökø²—åÿ2øsÓ_g»ÝꨎÈÞ@ŸÛnÁS-“‹h€v@—hnGR¾âå…-”Û ™Öt~5ròâ8ÝFZNß÷¬¬ˆ—¾&N~îÃÃß&Çø^@f‹EƒÛ¹/!?:q<:÷ô×q´4Äq@\ýp4suùyŽs~[‹¶ë)–ùm¤MªØ*Éñãöûdhhqfsí/-ƒ¤Iô’ögÿZ©jj-uyx”¼¼E!°Åš³@>Ðl4Ácß ­ˆ;ƒ„€ñ+`§9€4?BÊã{»z~¥§oø'àuø÷0v-³=zô.f’£Èž3Ð:3}ÜÑVú9²Æþ3+½œ¨p#àêâµh ·ÁýGQ ¸áŽ»ìÅáÐ"GW°HVupö›h‡[›'ÂÅ.­'å$–ÆIòt°N fÀKš¢Z­ã>mÛL3û÷ÊÚ\ykûÓø1ÉmD ŽëmrTG>Ã£ß ¿ÿþd«%Š._‚œÑèá6À–›ö° Ã…L(U5†rFƒøò¿<õIîaxÉÖ¨ ÖY­þb¸ ¹6AŸ´ßï•%æuÜ•Ò^ÛüDвúùoú_‘ Lº¶¤›ä!$ý0ÀLR`ÃãHLÃÐgÁ~;u¾?SÓ“_mEŽKm?âÑß~¡€8à8¶ÂO¡-V®Ï­ ÷Š¿fwÏÐO×~m±·Î¨aíý÷Á1ÃGàTØãä(× oÖ9ÀÑÀ"WÒ® üEߺ‹Ùç¶\pƒ @e좙½éØ)#·ŽÞJ;ž´É™iZÊû8>=+ü%W#óLB^ŠGT©ã£ely#3ÁCSVÛ“ÆíH¶Mf½Ž±eß+£µë—¸ï^ú0û箆¥…ÕèÈ©anWä’žÁª qÀ?ó@tþ¹§ð0‡!æW9ÆwžÕÍ©—zâ¸ãaŠƒk“M{È×€U pTm‰«f«•a.ç:.†—¦½:½<†ú ÓZaÙ†«À·t4×c¡¦@­GÓ¦ž¶‰°ñšÆÖ”ô {Ru¥Æð&îy«GŒñßðÄx´xe eà9ª÷uÎð³B€ÿ9þ'Þ+Õõèòô§ØV¹€àϯá­Â_ã¦I®Â]p4 XÛ.A—–ë“M+<*ž„æAñ+ Ôã´Â³¡™å;^™Àƒì’¶˜Ö¢Dr¨ç ,'CÈÀrÄï)þ®×6>ÁQ>Ùï¿%OX¸%l=4Dƒph>õ w´22qïϰðó…^9’³ýÇN¼‚í€~öù1ðcÉZÜÏÖ7ñˆuz7WÉÊMsEð*x”*7¸ZX–©¯]“£gþ££º„òÈàœû0ƒä½t0£q/[£"䔌†’×RD3[2 ®Ÿ«­LÈä”ÑúS¡ö9dVÏöÉJ!ÿ¾èî‡þ*^úæá÷+”¹Ì§b|/ÌÏÌÂë•hæÂ'ÙëÇ£¬6È4‚„/ô…ÚÈ!¯mPC Ý ¤üžû”}xL.ô@$î?fö/CÉàúwù&éÑð¢Š)a"lµuI®ËãxJó/¾‡JpåRç3üIéÝÇ’6ížF(yx9€ÃûíwÓsQþ[wïÐ娒ܱ»!&GûưTæ¸ÚNƒV0ä>ŽùÉM2Ì∇;Ø÷—+içg£¥E]0™^Õ:r+OÀ yt+À¦rÞ^Í^TåÙn·f&ÂW®ÀŠ>˶ÕÃëËŸ»6ó2*ìíy’6ÜžúB-‹A8Xßóvô¦ŒðßQþK¬Ü@­2߸ß\ T.±aÍô…bÙØȵµâõï„ äuã°ÆVÀìô‹ñ¤Ô i€Ð*>b @žÆý† ¾«å•@„|*åJªNA·ÀwUg±,X Ú¶Ñ-Mn,w}{t×} §IrÙÏ,+'WX9™Ãxò‹tCd•{LŸ¨Gª’G«Õ¸„©f¸ÖnC*¬´%¬ý÷q#74Â^±ÁPᯠ%˜[X’DmS4Ó„ös{x^®'t9«}ãÔ_FKxÃ>Yú¿­×–ÛZo¨ì`pà†—rB/vÀÚµ™—þÇÈÑ3¸“ý9”ìËwÿ4içf^ކGÆ¢bÿÆs–«™µ‰oö Œ»Y ×1,q\P®бÞÖš?H§¥²âèH/¡RI–ýH¦?´QŒ¥q1AÉ’‹iF Hã:p+VVlzå·-F÷½ò»¢kSø÷_†ÓϲÌ/·Ç¶y´Ó"ñD°Ëc¥Ÿ‹°™ˆÇJ€ƒÅiCÀ–5o5ô³,lyüžÚ´§^ŽrÓY‹Ê–3}%ãq]£:Û}f[@« nsž^}Ô#xí{¯}žV¥õ„0p`ïs ¬ìýo´W[X¿û§{ûG± >foð)2û™à¢ 9È~?ÐT+\þc–x!NXÂl}e骱H‰­XÓùZ—`Yqdß"¢Òi…+T²”P¹ $5î²dŸLF&©Õ%ÅìE›Vd¡”`8Â[ÅÒ0÷.Ük<ö5›k¶Ý*ص´ se‡öÍqEòC׎”—ü„o9Ø&×Â-žöÌýr-Ô²‚^S°íòÇ«±MN^I³ol19Ã_xL}+ý“ Ò‹…€;ÇûƒPsPÂW¼s}( ½çGÙxG±Äݳ7á?rw´Ñ ûûú8–¶ÊLv o€ÝQ‘åëZm#š›µ[2([fßi‰¬0i”¾ÓËÏ÷q$îJÈà¯ÂS…¤ÍNÞJ:¸˜¶Î"XRÔIÄäHõD|, ²ø–ˆCj¡½C@ I­Uèh¦Ô¥s¸¦)yå´Œ¶Õ†0µ„„ZʇµÆµ „F!taÜöÖiˆ”wAæÛ¶¶$®Í àþ&ßîí¥ruvjòñ*Jíö†Ioo½¡¶ƒÁ Œïx'{Q=ù#8 z NzS ‘aRÆÕ>ýø¯ta©Þç:n¡c;€*»pZÃE4ÓÏÆu$NªIòÎÕ'x11"JO`1Fž€PÄɧ"qE FÔèߤº‡e2-¹êÐv()!ì?Ú(Ó7ÉÁTîéãz}I]BKhUŠcû¢©]„ñlÊ;"–iI&)-1ÝŽûëµ ‹n0žaëç70E}ÿ̹'æ=¬= Àžùû²!Á`_~¶=ÕèÚõ« ·Þ;0ö¬ôßhë–—¹k¾Î•µÝ÷á©nS€UqÌ=KŒÅrÕ-5¸Á[öæåÑ1Þ¦ ƒdñ[1Ü0\!‚â—0J8®$i€Y—¸”Ê{Jºˆ ÄMQÐ¥XR¯á“@ó¨î¦ä´ÍBÀT.‘„ H„?³.0“!‘m=JA«’BŽbûò÷Žçú}ᯠ5­Í'-µyðLR1ÍïFZ¦Ú˜ày¦ÆJò1À¿ÊñÉÏœûšÜqžÀɰp ?ëéÔÐø]ßÇ•ÂÀ0*Qt0ÞE“ª\tôȽœ_ãl fVõæfTàzàé©'Ä40¦˜<~Ú"Yá`›âçjãZ1Q")¨•6;•‘-aëJAÁ7µº,)nôCSÛc äøÄ[iùtsã)2$TûP¸Òzcò®NÛ(È0y©B¦:‡ Y §MnëKó[sr "ôåÉ-ä단È(åZò=r†®Oœ¸ý_¹3üÍ_‚ÈgÙã_à^ À^ÿB{»}AØÛßg¿µ®0|äô÷bð;F ÁøŸþáhpð. Ûú8ÿÏUÁŒÓ×.OæRÕ±_CAJ7ÁÏÑ\_ $Â"…iP<<‰*‚Vàe å„’Mù -ÞŠ¥D|â óèì$*Â>쌂6t nk˲UJi¿ëÚâ,ÞŽÒªH¡6ͳô´vÉ‹úu¶(/¦L“~á)ó+h™1{þéÛv†ßoçnãíû·[Š¡Üaâ@PÓ×¾M}Å&à{zÇE 2¹JŠ]Ö_©tEÃwc 6ËÑõù 8»I¹Ö*²¢AáZµÊ‰Öí+Â’|OYA¡$Ò¡HAÐÏÓ†(ÌVçäœ}—™¢¹EB½¢i=Rƒ**Ü yEÈ«Wë×PšGܱ³~/GkŒu"äåLj©ˆ´€Ð6¡&RH.á·ÙÅãr ¾ŠíéØü&ûI…?âlç/ÔjëO\~‘€ý÷`ÿ}³½Ôâ ì¥¯q€Ú‚ðw° x´X®p1ý?Uî>r% /ºtî«8JÆk¨7qOJ:ýØsÿ ODJBEaq9;U´I8’jAŒK´f*nRA¶`%ê‡>­›7ýð{®Ñzô*Tš-¡­Ñ.iZñ5ÝR 9LŸ° ûüxLˆòÙJã<Ѷ§Á¶œÛ°X ïýúMfûOß‚bšÒ^Kå÷w¯µ2´g¯r ({õË€v Žžúnl>\ªTÓJ€ ¹þ௃½ÂÚô½P(E'Š®^>‹O{Y¹QiiØ´ŽôÙj|¸ÆSMˆ§Ë• [·P˜Ê”,ž#ªúI¢W(¢ ¸:î¶%¨È)$t@ò%.ÁI!Z¸{KN–O’¥ðö%]ÎOÛËR cìçÊHóPa^M^Ts%´|ÌÏ´Òäq†¿ùîBT|ÛÌù''¥˜”ÝïOPöû¼³í Àåÿ¯}`äÄ·³ð”€ã±ôÈöZ†bøó¤Ž‡_ÄMp·ÀFo%%ŲqiY“—ž>¾à:éb¢ñËoo ÔˆfJZk²dbÙëÀ¶.u¤¸--PÂ; µ_~(Ü0ä©+V ¼úbXR•´J0äÑÚTòö(x@‡Ÿ, +V ´üXNMí@FúgZ``ÚÞæËÔñÛTþ®ÙóO^̯oÿBƒ°¿Ý^hyPöÂW8àmèšøÖþ¡£C 8ÙÒUØU´dn†FsdFLPm|)§Í1p'H,q‡©9¦èGɇ9âd1ä­­•Œ6å\™¶’‰…½£k.mhõޤN%£õ$9»h¥ÎìMu¼²Â_›¡•™ÐÕè7Äo„‡›/øbÃ>Á|žoüÅßƒà¿æ=PÑ|>¨.†ÎÜBà27N8ÀJÀ_ï}´Téº+Ëö*—RHfÖêå«`iCÇÏÖª}£ù‰,v¹þìX ÄÈ^ý&ªt¨Ec¦ Õ)&ÅLð-ôßyŠ@\…ÖÈÑʨžR?¤VÉÕni(ð?*ð¥`[¢~FN ¶©qF'açò(Ñ”}ý‡¹Ž÷\Ç+Wè§OtÇCçn ‚pS؈l‡x |7¿Ÿ.W»No{8ˆ)#©ûý¯À² F®BF¥–3’:Nø%Ò-•“&fª“|ÈØÚV"«Î Íf é㸤;Y®öàá‡GeÒ®«±$'9T°»’ÝL5iÂ<Ͱk¢‘e.i±5³MY¥±PI ®OÎHm (‘øÑâJF¶TŒ,_ØkZÂØèOížÊP"ø3L„^S®ãýoú9¬úE8TO‹CÕíÐٛĠÜ$F2ÛçÀÀÈñWp:àåJÏ*·_z L_ø‹ìP¨p…eɘY¨“Ô’Ï<3H“­2Ô£èüºFà«l€€OCëÈÒTI-›'å éÝ|&È´ŒþØbM9Ãÿ>oo4ê/\¾ôlj>åÃÏçßáè{èås (7ÎÃ@á8Ð;0~FŽbðCÆÖ7@×IÝ4#„üPá(¡ÿ!ªbÑÏpñ-ECH¦°V™PUbêKM³ýdz¬´R±8–\í©Æý0UJ…¼Í9~‡iV!¥¥&@/Ó‹úè"¸ì©ˆ4‚'ÐÈhž¥Ì;À{ï̹ƒw†ßçÇnã¿vK"”;Ä À!þø{¥ëlíÿýJWïkM›TRIBäC^: 7Û¼Tòf…½ ggÍmH°Ê*iO¬(øAò}xÒ ·”í23Hé"[§¤¸i‹¢v¦×9Wiø¡– U ä£Åq/Ó‹ÆÙ.ÒNh98%›Ïó÷ÖF£ñþzm}þÚÌ˨e©®t;^..„Þî–AØ-çB¹›Ê®Þ¡ñ¡Ñ“f%àu±P󅛯%¼‘'«¨ JÂV´S¢H~ƒ4íâ@g»J<ƒ§à†1W‘W>ΖmaŒffô^® yEˆgÿ È [ëÎ*?Rª½ B%2$šÜîTøGù~½Ñl|äòůø3üyÜÜ)¬=_wJ)àFà0~õ=ÚgQ0 ü86ßiš¨²i+i¦xÛê— CÐ|-#Ä4+¶Eõ)Ï ^… [Ô¾²®= ð (LBC\Sœ—*2NIŠ%ô ªG#ÎÉ ~G[+a©ŠeYq¹–Ž‘ÍŠïH«¸³Gñ4ÓÌ“ÄvbR¥£)¥c#F¯¬¢Hã¦]æöÄD-Z&™*kŽb a[§Ñx’†Úâ±Ð—dS¼ô}’¿_®×6¿re*~áÊnŸ ì–s¡œp (áw°W9Ðää_ù­JWÏ?o«äµ\Ø­xR5•HÕf”Ú ¡‰ˆCßõ¯À;HÙ˜4‘x™ß‘¢]EUpª‚’~Ìì_@Ê0C8“J‘ŸAÑúm_©8á “¶ÅšW!ðQo)–«OObç=)¶Þ`"(7ÈÀC^<(‡ü°Ç»ß7vüÁ÷²ðÏŒ ÚŽ„Sy&S u3:™~YÂ^¦ŸåÀVfÝñl£Q)ašî¼_ßU¯%­è’¿ ÿ4É ~†R&©u:M§¥¬mº)ÄþèÝ|Œ·á¼g28ïɰê“A¸AòâåCÞÿÐý½ÍåÅ…én6Æ7º{‡(™­vh´Èœ¬ è>\aíÂ,-kdZÉé—!nP=-DçÂI-F»&X¸«+Seª9Ž@[j*ðOâúxQ ò^TÑ%4à”bâ寳}5.RÙ[ù{/×ñ^ò°B4p p`p ({äC„fäs`cåúòJ¡ô£,‡£ ýŠÊ¾ü)åce„gû‚^Ž_^À±@Œ#fò›ÛP,–âj¨ô³i…{¡/d=)©ÝÉ6Ñ+ ˜æP ¥Um«n›aÈz3úT5†`LÖÐxôAn廜AÉÀÀ=ݰ‡>FhJGt»ï]=C?,ä’G„Ž—Vé˜ Ø˜¢dÐM¦‘[i2©âšï#+,SŸ’·ÙŠ$3ˆ© HĨi -¿l\$KËOû³žTh¡9Äb‘þymÃüßä2žæ3ôñ—šÆÇç.>Žò¥x}ëa àÖñö0P ÀaøÊ§=#gÞÀvÀO E‘H;{T†eKФS˜Æý0U‹‘xF|=Ä&x—#yYƒ¿TáÖ„ÖãçÌ è$ÛÒõ›éi‰«à×>· ˜J,Ô‹úh¹Â%µÌ/Gù¢/ÐÖ·K•ONO~U,üÃs9ûncý¡ªýÍ ìïïw[_™¸÷WØø(Å„‰¨40#ø20Éh+JícгÿÇ(F ó’8þ»¨DgÙ¦¨´[ž|:s·»Ç/ä•ÄÝÓ^ $Ç–a•ÂFû~Áÿy,ú7´|o/Ú¯ÛÛŽPÛþä@Pöçw;ì­.NÜû xü»#ÉŒðÜ›DÖÊ“GÊæ˜L‘ÓV,[ÄäB% ÈYBùÂ\±ãÐË–Í4Ëßßׂ*ø[‹‚‘ô@ª{ä[ôÛ œ Áÿ)ðÞÜ\ý>úYúÏä@Pî$÷÷ÝAØÿßð°ö ÂJÀf%àçP0fõ$ZÌ_(K¾ŸŽ‘¶ˆ$åòÛ¼z·"I5ìS{†D ›ÂJU[¬éåXÈÍÕÁ%ÈÑ?L•-µi™äÌtÄ/ÿ£üýz©\}bêåÇÚUh¨…×íã@þoòöÕjÚß Àþþ~‡½õEV~¶«gðgÙ ¨´g†/F5Þ;“–uVh*,Gª¦ ·¦¤¨)FÄ(Š¢4mZSk0 €–×0® !Ô†H,¿ÖüFK¦÷(xѸ9Ôi·-LlÚ’ 7/x; {‚ÿ|‚b{AØk_dµ'(ûë{…Öæs 8|ôôâtÀÏ ¥]UÏGõ¡"EåQÑçKÕ$Oät’òñMáí¿´­/5ó¶dÅoE+ñ˜H:KÙ&[ó„E3ŸímFí—/>Ëì?<{É÷Ûë- íÛ‹ À^ü*¡M»á@í€ÇvÀ¯±Ðc&×P1ʲMÄ›ÆcižDD::)Ÿˆ_J’&ß ¸†8’§©@„’VA/ˆ†ŽÔAD {8ZÜ€<2&ÚÖÀϕҶ玄ÖkšeÝ[‚ÇhÔ¯bÑÿ»å[ÊVÒ{—ñ÷Ý»M -Ûà Àþ8¡i;æ@…•€ÕÝ;ò†=¶´È7#}M¨rÐ(1yÍ-#ó ÔŠÍ„,@ÑR¤-P[$E4®èY2Ö£_ 4)™4,…dèµäÙý}Û$s†ÿ‹zS³ÙøäÜ…gÂQ¾÷G"(ûã;íÕV`¯~™Ð®Ýr 00zâG{ûǰX¯X%i˜Ø´‹øKOÞ=ý ¥ÎôäÚ‰iG/í‘°¥¨éÇÄsòÈQµ)íK*•á}`&ßÊû г0—/Gùþ4ŠŠ7›ÎR?éðìW`¿~¹½Ñî ìïZq“9€€14zò]¥Jw—›ñfj°Pë¼Ç®Ì‹lÍŸj‡ W@ KÑ_oH—”ºâ¥är:í‹+KºFºçPnK5ñÒ×ü=xí+qúìµ"°ï8€}÷ÉöTƒƒ°§>GhÌÍäJÀôyWµg`ÀеS`'íójR!/yNØúhNF›=|W–ÈÁ³<&ßr¤/S^2óÄÍÿ¶@âCG¾eæÜמ¡>1®>Dö'‚°?¿Û^iuPöÊ—í¸ÀEP÷k†ÇN<Šqàý2÷Ö™ºµÜ¢J•)ÀÊNìÇ•šÅÒT»P±òTÍ£¬õ)™_ \FZ4/£¨¼¬GŠåÊ‹÷µ¡äS ñýÆô7ßo­í½Ó Àþ¡þÛÁ‰¡ñ»ßÙÓ?ò —™‹Íþ>þá9ŒÈûF>„>ïŽAØßB©}ÎÁ±S£•jÏ#åJ×ëñX4݉·$¥ÂºSG·‹ãÈu"åòZtÌí Çöšâª÷£4í›+/\›T`”ÊAä@ëïå ö2ôéVq (·Š³î~à@µøØôŒþ8Çä^ƒ±_%µ¬oz +Ùî¨ðo*¾äó¸À&’·{KüšcvÉmÎQö‰ü6v _ärž°Ì¯L ¡*‡»â@PvŶPè qàØ=¯.3’~[£Ñø)”€ïåÏú Èv². x"¯'GàÇrÞl!(¡ù_ Y£Cˆà¿ùe¨ýágfÏ?Œú”Ç!Lq ¬¤Ø;ä@PvȰ€~p90q×+‘Á…Óôðùû1”‚ûÍEs Ðt[²[ûN,÷%×OK:òŽŸdÐV¸Å‚ Žzš†Àû9»?ÉÞ~ðÖs-Dò8ü–òr,p 3‚Й?!÷r€U¾F½þÝQÔø÷æo€ Çÿ=¹€Š|ôFC}{Õ€„üçÙË_#¾èðsÐ}´?¢Ä—¿²)„Ûá@P¶Ã¥€ÓŽAhÇ™p˜8ýª>äúH³Ñ|Õ€W5µoj6¯Dxñа*‚¿.+ Èu¼ç"ä£è/ ÜŸþ$4^o¶Ñ¬_«m®¯ÏÏNn’žÀ]q (»b[(ä8€ðSØääÀP¹»¢R®à[ V®top 1½¹Î‘½ Žè±—-³”¬õwÇãPj ` …ìŽ8 @džÌÀÀÀÀÀÀØóÏ-à8È ÀAþº¡om8ðÿ@wÊSÙ&ÊIEND®B`‚icnV C CubicSDR-0.2.3/icon/CubicSDR.ico000066400000000000000000003301551322677621400161550ustar00rootroot00000000000000 gjV00 h&½j  (%‘ ¸ M¢ h¬‰PNG  IHDR\r¨fsRGB®Îé@IDATxì½y°¥ÉUØ™w{½Ú«kSõ&©%!5R# íBBhŒ`l c‡± Ä‡€0„À31¶ÿð(Ìá<1L0`ã 6  íÚ¥–Ô{u×¾¼zÛÝç÷;ùå}·Ö®Â-©»yùÞ½™_æÉÌ“'Ï9yrùòÖÆãqz2'L­VKý~?õz½?Yžíôo.†iœækÆÙA¯¶1ŽtæR ”Æü=ÖÛLËV«]« ztnƒ¾}¶ºÙÙÙm~½Aç6o–Šàw»Ý433“~ý×=ýøÿx:zôh:uêTÄÝŒ¹QÛiO-jx…½«Þ®_;Ÿ~ôö¿ûà±ïÜ{àØ›f›í™ÁpðØŸ=ü¥ßû÷⫬_j.µÛƒõÑ0¡ ÈùÌW£Ñ(5¨öïߟ>÷¹Ï¥¹¹¹ /?µÔ~æ—vCpeóýugΜI›››IŰ­®¤ÒÓâ¹~ªV½ó®ûnû‰ç½ô×f;sß3ÞØÄÔëÖZµÚø¸ëþ¡‡~ñG?üîwþÖÙGšÄ Öµò@ýÉíÁ§Eûnˆ„Öª|yîܹÂm'¦tK  LëŠ_â·ýo¿GHo«^¯õÿá»vÿôËÞðGý͵—ôú›Ýv£¡lÈ`Øo`Ñý¯¿ùß·wøþßý¹ß>õH‹|}òÕêÊÎ3Ø(Vkñ¿y=ǫ̀ùo¤Ê¨¯_ÂÏŒæ>{±DºUËc„¸µ§Þèýo÷½éìo¬¾¤Y«­ÖêÍ…4ªBs·ÍÔíu7;íÙö?Üóò?A¼w8µHï£!,FØg¬+ñNû¥õþæ€õ·ÿl<¦øµzwmÐKùSÿÏÃ_úµKŒü3õzÏr¶¥ÿYÀ·Ð„mp Äz:ƒ"¸•{Z«ùKŸÿÈï󎹙ùYü8$€~` Á^ß°3?»´úÝÿöŸúÜ‚ š›¡CþÕOçvnãöÔR`[<µôü¦•VFowûµ4ø§i¼í}ïú—Ÿ:ùèßIµÑWÓLÓam¸™Z©öî÷ÿê+ßøßû#n ù¬ á›Ö„튿 ØVߢ½ª,C7J€5þÚðÝçŽ/¾!ÿ½ã[{ùíiuOênlO?û©?úÅ7|ð÷?Ù?ƒð»nPM¾^˜m—ût¥Àßè$àÓµ1Ûxe (Í#ì}NöÝx´ÕXøêL' ægÆÎwÒgǃÝB²ø§ðoK¿Dø[ê¶À³°ã‹%À«¾µ:|m8lB!œà´ÿ…F=-4š36¸m ðYØÿ·Ò¤m¸j=Ã`]tÑïñùùÎ ¶üVõу<a<\²)£jÍ¿(ŒgXó¶Ñ} (°­ž">m‹àdߘóþýµµN‹‘¿_«=ì³iÇlà<9[Øv‹)°­žÅÏ‘ßq“÷ƒö´Ûs«ýA:2è0ÜïØ±cžeB o+€gqÿßLÓ¶ÀÍPé Ñ߱ۂ«íöÌF»<ðsz0åv@xèïàz^ô…èø:à ï/¡zµÆÌ€g̃^6ÂðРüàÅ@#O0‹‚,Œò‡é8ã·Ý3—Û àéßwÓ‚<#Ðêp„Œ"ìƒÑhÐlÔ{sˆ?/þ'žÓý¼{f÷ÌÎî{°×Û·TK»–Æã½cÐ…<òïk6nËw¾‰7€Võæã÷ûgzøM¤½ÛA¬ññÕbŠS)´±(˜1pßèÖM!$…›(‡êyÛ{†Q`[<½;LA !Cð ·yk·Ï@<`䌹ÌóµwܱçM·{Õ……—œ ŽÎÕjwß>7lg³¹oe4šÙÃð¿ãÔ™´ººžêÃa{³ÝIýÝ{^8·ïÀŸ’Î AÚ¸8ìŸ9qÏó¾0}uç(=ü—çÏ~¦væìÿð¡O7¼;áƒv³Vïyíx%ùüžÞdÜÆîzØV×£Ì7?>„«Q¯7í¹.º {hÿþÖ]ÇŽ½ü-GŸóÆÚhøÚñÜü+—ºÝHiÚÝj§]ê‹õõøu çñ[ü ØpáöþÀë€yý‡#Áì Ž†Ý!?Ô8=ÏͤñÑÍÖÑÅzíµö×:œÖ޹xøyÏý能þΜ8ñ¾ûOžøÐ£§Ooz{5`HÔ168Yt¹çë¹Jg\/y;þ›Amð¡úãJ BP<Äç…ð›Íf÷Ž»ïÞ}ï¾ý?pûÑÃÿ„Soùê˜âkÈîâÌLkq8jr«gs1~3w·øzÜø»J˜Á´ˆÏ•@É‹ÁÐñ óD´PëD¯±)°V¯µ¼´ë–á¨>^`}3¸Øïuêï»sß¾û–n;ôÏ¿ïèÑßúóGþ凾rÿì8¹¡àš‹ˆ^GÑ*Û.üÅ̡Тø$ßЕò¯t³e\+ïvx¶*€1͵:þzŒt½øk•qYPp( €Lâfä¬ñ‰ ~`Ðgç{ßõÆ×ÿàóþ7««—ö×7Æk¬êÏ4›µî8µæÓxÆåÀN’qž¼CVøW©¶CH1V(œÁìWøgüÑL®ØD,M£¼Ôc#…’."žë<¯ƒwŠÕXa¬ukõVc¦´vƒ¥¿ ´Š=DO¶vPÞ Õ ú£Íe~oðßþ—ß÷/éÎ!³§Ñ\'†ƒá©nwóô…óçO³Hpj8üdo08{ñìYôÈ`´±jлG<P«æÈ£R]ó›¡’šªz…à|͸]çrþťřN{f¦ÝnóCi‘³ø ²‘,‹ssó‹KKKóä]¬5óÆÙ±ÀÚ?÷ر—mln\é÷º­ÙÙ°íޝõò:/ÊáÕ8Ê«šJk(GòCönFúK(€:~¯Ù ë` :Oú,Hž Þé§ÖÆX 4_Jœ!m°—ø¬EÑ¢¾ ´É¿)ºÂXÇX™'­7Ö9ZÜYkÔFíþ¨!¥;ZÍLîô]ƒ8 é÷:˜ü9Ò ìx< mëüºô¥Á`p†:N"ù'‡ƒÁé³/žê®­áºÂÝÁðúàì™Ó§/öûƒ0P@ ‘ñ·i7õd_{ÊÙ*åûyÄRÈh³Û·Z­1õް6ˆŽ\ùëŠò,Û‚¢Cõ«Ç¼ê{ªú«Ò¾)¶¶ò»7/Ö7yëM’–…HÅ¿f)Ñc|‘xÝO鸪-ÏÏU£nwÍz.¬ÐCˆÊhOÞV{f¶½´c‰…óÆL½ÑPp]S[äÒü ¸8¨×ZÎ"¯Ô.¶øÝ½‚Ë„|q¾Þ\b~öþŸE¸¼ÉÏõÕ=‚ßöºn¦¶ìœ,»×îh#Z„›VÚ³kOêmvGŒ®œ×i; 7îS ÂÀH̸'©ª|#€ŽÅ»töׄ߉2@I$-„¯R,¢$ð2xÿw '¬¹C)à+ø¬ê1<³TˆÕЪ£H(gˆ_Žkî40|Ç Ãá°Þ§m}U¥úrÐwëµ!g Ç Í–Å¸Æ5¤Rb]€ê /͵ÚK£vûÆ”wÆé¶={ÓfP`‘yA—‚øáùÞpµ1œÓýáèD/[g.bU`eœBšŸçSk—.]X]Y¹Ú\sÀâ̓F¡ÄˆKCh3ÓéL„"³X^§ Ù.¨˜D*]…o¸˜%†oÖYÖõÜ­”s½2¾.ñOµ(D°ÁÑh&yÄ1ÜÈ ì%ûc8¿¼‰6j 7çò¨+QºuúnK{ayyfaf¦ƒ€,´uX ÝÁÀ´ÔLyÆ.,íܹÀoç-6ZÍ2.4ë%ÎÐaÝÖ ÆB»0j694WkóÜ!¸§Ù˜× =pã½{þšûsáø±=š Núi«&;Ìé¼Z" [ŒMýqÍ•3Jà«^ó7¼¼¿¯¶sµ|™²ÓÆ~`O<5D‡ÍÿŒ]¯wC¥Å³‚k¸Ex½JŸ£¾U2ÊÝd|8Ñ„¶žˆyJpžŠ”¦ u´GeÀ|%-‚©ms‡€86Ä¿> Ú ¾Xd ¹^\é®a‹Û¿n16Û¬@°P,*¥°,xì~~h$‚¤»c·'ƒn[P?XToÌÖ[ͽàû<•˜8Øöç:4§n†úq۱1è_Àâ9.'ǃáÃþ©Þ¥õ³—.ž?ÝžŸ?ñÛÿñ?ždT<»¶ººÒï÷y Br¸éá)S³tƒF:ýòÉ,ªê¬jÒñä‰.#NbB:æÛN¿)—+´Ô[åºÙü—Ub*ºŠîÂ… éÍo~sºï¾û8!6K@´®¨ç²üOµˆ…É­xBÇÁ…=·ï2d.HÞÞ½oo‡w¡c(æò£ÙÒB»Ù^XÚµ¼Ø¨ÕÇÅ6ÂZG˜É»LëòEìá¹N«=KÚ \-„¶Å(]+Å Õ%–Ç` >Ž: aHÁŽÑ•xWÊeRQ>Õä|C/& D²j£:yí`æü¡5öð…k-VëX`“›$S‹™ÂfžÃŸÛ½;ÍïØa™½ÑlŽÛ­VŒø*Ï‘.f%Žt´?‚9MA”G¹mÂ(®xVIq& £–cœ‹…–«²Pq‰‡4RÑèáÓˆƒFZõû©¦å7ƒ-ÆvT’Ï#qâO½qy³ßõ§É¢î(,B§PÑâL¤ê¢å­ÐG{FE‡•âï (Ø^Þ…™Gqt  zÃ(}û¸Ón-Ñ?GY à%Ú5ÚãÀp,úñßüʯŒƒþæx8XYï Î5ÆÃÓƒÞàDo88uîÌ9Âݘ’PÏ *:EÌjB7úû R~näl(„ÎÕ÷Yrú±ãô£-ø:ŸoäÌ_Ü“Á†p+ässsÿXOóóó—)€RÐõü§BDƒexaË™ùÔ¦Ân£Ùßy÷]Ë SÏÁT=2Ûî[Þ³ûs×ÃÌÁ {±5çšíÖ,ÂÒE:›^b#!£q :Žòü’5 IœŒŸ})™é#•eT*~ߟø†OAxÃ.š» Y^…µ Áu”q4’’ƒÃïóaÓŽA«O¡¤gä¢jæ\Âö€ en*…J"˜¾BØBn°óCñlzwf&í¤ãT ³‹lÚá GµƒÄôƒ9;,o,ªñMËmæF<ò³e9òkJ@I³ ŽcÀ©eÓHÆîban(=qWñ}ñ…0‹e¸‹`Û¶èúh/u2”Š8@«ÙDMWŠtuT#©˜1ÌG|Ç8D¼USÍIÿœŠ cþH©Xߨ£:å§1Yyd©dtAS0ë0š4”ÃPê¢,dÆë;‘ÆmŸÔjq2”-½ µ CA'’ºÝÁ`mÐï_ ýgÙybÐëÚXß8½º¶z ká ç)XJèözçÎ:µBÛÝ}…õ Α·X´ÖJâÃY,“ýÝøð-êªFY…µR–JÀß¶†b¥Qx›TâãûŠ/ñïv»iyy9€k·âþK¨b-hÆ›Gžs,-ïÚyˆµ¬·8xè­Ì‘ßИ¨.Ó Ž#ƒDŸŒ€Ð.F;‚m? sL6Ôªˆ ãÙ:u޶ÌNx ó£72:Ú‘S„胱ÌDá„„ÕEÑéRÔ1¿B+Ãr‡.uPªÕ€Ns¸"|žqž aTæÎúÍÇsÍ8üBgÔ!EyØ™¹OOc3GmÃñ. ”ø³|Ú˜t6ÔÑ]EeŠô.$ª¢ ñ$N%é.åHÓ7ÒPÓšãfÄÕëM²n)ij0æ+N˜PqXêÒÁ'Û¯“¦¶a¤ìLÎ'¤½×%!ÉË|Šô‡^Îg1VN†ƒðö©ÕÕÕ3+.ž¦>v>ú'°²Îl\ºtq½»©±¥>EÙæZÎ*%±èÛÂI†âŒ×MâäÛ¡ù¯³¯oÅU|}+Y&°"3v‘ †éí?p`á¥/ÿ¶433ó½­vëUÝnoI󴻦!»þ„|!·Ñô—FLÌS,èÌpa*›&Et¶Xdeã¤b‰N…baOXç·ÄÑcöL½€ÉéÂ:*F=Àe–÷*íÐÝQÏ49Á­fZ¿y„/8‰·ºWxÍÐÜÓ˜4<Ë}šýÂû¢JG“Úú{J}s3>s&í\XHCÌî>B?Ç%‹XÎóg+”ŽZvòЂ*ÁÈÐZkAF°h¡ AX6"bªÀh&p˜þÒA|†½ k¼[‡ÒQ%íTHecŽúïÃm’/è •XÊÀ2a 1dùɤ-(Ìþ©íæá.8ÏÒZ^M ú¸þà‹K:iÙµL§ÿý€¯ 0=ÆMONÑD”®eŒ³çlÅŘÁ÷ Èí1/œd0 £<ñF‡ÅàÖŠeÙ/öƒë ­€‡§žÆ¾À§~5YÌhžÔXè´; (õƒðÀ·È—&†V…±(q£.ƒÛ*tv—–)ƸšjÔNŸ;{æÌÆúúœèü'6úÒ—¾¼¦I£PkIäâòøEƒ„ŽÈ|qŒþ×ÿ›¸[R[õO°á?zǯ¹÷¥/û5–®^°Ùݳõ"‚, C¦vëÚŒGÖÅÉSI­ñŽN£m¶ÎÕæ£7q’DXsUéÞzH*E!…Ìì¨K™yÔ!ÁQf@³çÅ/˱N5½‹j ,=0Yç1˜T8óZvéípùÅI†’¡c¤¥,ae0U 0âïÈk¤ó–]Œ(}LòõK—Òhu5­7Ï$8+Ku„S íB€Óì ñÄqtB-Acˆ@«‹“Ö¨ÓF ^ÿGÑ 0סû¦´ .]àÓwÞ-i&ל_Ç ßAÜšŠÜ‚‹>¹Yx™rÒ¾÷ÒW¼â¿¿ë¹Ï}'« V½vG–wWkÔ‰ùªÚ“l%hª;Òå†Uª>GMZTÕ9éÈ7Nâ“W¾Ù4õ QKšŒ«3ŠK_SR_¦ÙÂøäY¡×°‰¢ò–Ì‘fc#ßQ÷ÙSt¶X¥âaÛeâQFD’ŽÁ£›«ðBv!„pæß ÂòÌ‹ÄÏllĪÔÌç‚Þù]+ß«eÀ³mYftC¶S6Ýׇ€B_WEQQö,ñ̽°883`ÂÜb=vu_Åq‘0ö;·Óyp?@(‘yâVyÆ E°6×V> ö Ä_°üE¤øþ e)˜î*¸² ŸFКz|ê¸TÑN5 ½§„¨¬ ‰£Ø ëN„¤«JU:Ù¯¦›×6ÖIGûnúÙxŸËúÆ@`_êâ22j*õÂY¦%íÚBðª8FH/–„(»Ã<Ä)Šý„¢T;Âg…"—†0°C!£}bWÒ®^VŒj4jqväõ¾aŒ%wûwŽ}ÎWÏŸ?÷ÞÇzø}gNüÏXwkN³pŒ!ù |ˈȨì¿ä¹›v¥4P¹jû­o{Ûÿ±´sù혜½v«mi 3˜,¿¹— fp®q´-¦/1Es'ÌcdrNÍÎá ƒ—uX–Ïeä!£¯JÌtOЕ9¾í±GÚÀÏŒŠ‰MXçö›Ì,Þ¦åiD>Ö/Ò*ôêÀfó,¤ù^JËTιƒ0ɵt´XêÝ^X2*1‡½yòÔ¡—£þB»Ã!þåq~Ñá— »4ºJø å  áè}ϱˆQÂF¹, @”ðç Ѐzœç+ÔŽÜë„=ˆÇô²8 ‡#ËÑN•€é*Yçõ)?$!”¢ôª¦ÑBH[a¤VB<–n*@}ûײByâÐ! ¿›Ïž²imáó'`…)Ö‡i ‚nÃq†_'ÿZŽZ@XT1·!ÿ¥>DZÍ[ÁÈÂQ\2çsþœrÎ>P»Q|bjÀ,±öÜ]ËËw/ß»üãœÈÊÄõuÅ·ñv~qâç³B®0‹ÛÄñœ™ Ÿpl ƒSPŠÅàn„ékÔ&m †ÞÑËœâ§@ÈàÖyŽÏ2°lU!Hµ´“³-¾Ð£UàšÁ9> ÿqvÂÂé‘Þ!Üî1Ëõ¤àn¸HÅÙrªÀò˜Ó&´UÁ¸7§ðC<ÍS׆+$„;`$s»m§uàó92›n^WÂ=exçæUÝââ “Wt£pWÎÅI.PÐZ½s`ÊÄÍ7û\$co5]€~*ÆY´Ÿ»ü*žÐÊ—‚\òRˆfp:ÆQÅpð¡b`˜ÙJ˜Ú˜®.úçÙiÆ.üñ ™‚î; +ê!Äίf×Rô¥…#»kÑËÆS0Ò!8\\@䥕飅$-¥J—€J@Z)øž`…>„v_—m­L£…uæ£ÈÀÝòbħdëÎôÏõF¿E{AÊÅC„Å×AÄq»YI‚ÿ2‰§4Ðù]UU8"6‡&V™³G‚åÅå2ƒ*h6³5gYÈ +9:îo` ý'î}ÙË^ð×ûøße@£«°íU2`ù7r7«¢ ðš¾èÅ/þ§3™¶¨ºœXÃÊt/3SFÞà1ÂÑ.zŸ…"¦ÄðÁøÓÖ|2bDVÐC¨KØ4\´¶ ‡2 NÁË©<ïÇ:tjò)ºG'Z®q~ìTae–R†¾iúšî 6ÞR lä%.û”G‚L/ÛÏ:Ì£"pTt$uÎmÍ <Ëäv€B¿—°f³‚îùßU4ŠsyOñmR0ÇÓ‚>­w½à"B»@t Mho£Z#]^^Ä*h0²¯zq0gŽA"‰¼6 ŒÂì¨uVpúÇêy”õ å9?¶ šüÜ.ÄzAž"¨Ïw'ùb2gÁ§ŒÚÒÀª|óÐcé2õÜà'lû(•XL„£&ÁcûÁxG롚ø¨øBa|†“¬ˆÿ|«³ìP „MW°}ÎåA;,Ó<ÌÚ#\|ãt;Œµ“%òU&#õ\ò9ð3 £&ýì Qt•õ;Kg°Ósiü.!ÑFø³¼+ñzÖßþàþ/~á{yðÁ lMDqïRÇ“ù7£à-j¥n¶‘¼dò…þ‡çÏçÈç\».7ò¢ynY¹ý¤bˆm+(§ðû‰5ÒÜó×,u‹I%  a'ÞeA/ê»j-DÕpiI óD|eØ7‘£ïà•R8B\ævël¸m¼æÎ»ïþ“'NþW2¹UoÎ:ß ´G{7–vîú»9;C£ óDŒh^(è~´ U987æX+•"@ð§ŸÍEèSÓÉâ1ÕœÖÎ Áôj aOxR4:Þxµ”㈕OÆUÌý,WüŒq»ª¬Î†9G¼¡ßܘüSã#-3\`W•e›¬Wu'ëh[–4µ $t„‰F| ¬Bi[Øï‰…Cã­Ûz}a‡óÎaZï#î,źÀµ—v~­Rp ‹a/i µ J …ÀI3æÐ´z)ÔæqÊ¥‹»Ä (ãjý Â*'ñ°ð¤icºP¸Jùë0¦srwtâíTC¹p¤£SÏ:Ìrb”“¥ƒ¸æ+MÔD¸¥³uè,Ǫ*²¦ÌÒ&0S°9“Û•@DÁ$V—ò¦…“£d¥(ü qYT‰›d.àDÍR¢ŠM¯r׈ ˜«âµ±ásx„Ž“´l/Ö5’¾ãßò¢_¸ÿóŸÿçŒÏŽ7’Z¬®*¸«\郫¦#dtÜÈQýàÁCÏqdtØ'––1êÛã8aýçk‰Žô!œUZU„„ù^ÂU~ËÞ9eXø ¸Œ;±€ñ[XÖ¸ ;šlžR®áâdz~)/`|ÖܹŸ‚ L¼­§dâdâI=–Ï*5KVÈõà Á—ò2©#¢ n^™[xò”Óž‚×ôu D|€1®ð¨sfëTU\Ú+ì³TêhoõY.pËl“F­ ZlßÏâ ؤÝùÄê8==:äÓ¿Ì e¯ð¬â8Né34XÓÞU6«¸3€v‰~¼?@™¶ÕKDB0ɧ©]p'¤ ‹ƒ°ô {¦¾8‹²ô3¾8ë×Éá%^]-ñÖ˽I±…8¸^|Is½ò=I ®ÅsÒTÓSI×L.wSå›@:äÏ®ÀÆsæÀOnšÀLg(Ù̸Åsò~8ñ‡ØÈ|ôu´¾$AòàñzmþµÚŸÚ·ÿo>øÀ_FîìI› Î¥]÷ûf@Aß¶‰sûwó¢†BÖˆÈÚTšôøÂè|ŽN@iH¥Èë¶” $Ít1Õ)¨Åzpš ÀXVd‰ât!êPéO¾°Ì¬ÊAø°4“alwtÃqøÅ®L¬ßmK¢‚Á ä¯|ÄÁUÛî .áb5XŽéƒŸËÈ~Œº“´¬4œëÊÐU!¤*Ÿ¥KÄóå%gyvÞì±â92i®K|˜ƒEA Œt3=úvçʲâd«Ìû{Àùúí*4q×AåàÚ€ë ðÊv2÷mÜ,¯Eù5\ŽèIß¶úVdyâoœÎg8è,¢|ÌSâM3¬@›9xãzM;‹S`·Éññ]ÁDù9¢d«Z‰Ï9JR~2uªŠ’â[‚Z"…#—[\‘h«]%b:¼Q^´Ö I.¨Ï+g>0•’0³Y{­fcißÿð¤Iºªl7ônF”¸CÒ7öZÏ‘±À¢çÒtœBBOØ)€H*”½*^ÁV`yë/Öê¯ ÅAóÚ*-…2 ðYxÝ%bÙ*GãCðõù¨(Žta„ÕJ| ca Y•›wŠRÈ Z.°±²î.Ù¨ƒÌ9¼ÅÑYÁô”s›—ªødÌ`tÒ„)gÜ F ¢XŽ˜RÖ9¿¯Gš_Æ-?w~<ÀS¶{0µ~bužÂ8qúÐXi²Š0iÚû ÁŽàæ‘K¬ÃrìqÆÚÜËwt—AœÏûÒ²°¼U0i~±xˆÆ™®ÂñÛ§­ïBO>šDœâ«hz=+*ƒ“!gÌùPW¥œ_"¢ð+FíRFø%cI?L»ÉSÕ—bj ¶À&‘m_5R¾,À©ÜäÖÆ •3徕;¢?JÔå|U¨Â#ËV?gmfÐò2ÐÎ Ô/3è}VÖÜÊ¢K¸–»)€pÚ÷#nÁØÃõ6ûq)#~Ý?tPßFëÛiA3“æÜ_¡ôœsXyÁή˜V6Ò—`Œ¦°¾gÝ'ë y•®B*pÖ§*V‚8e«‚xÈ\(k[!ÏÛ‘Ä;ý =`‹¡yiO°Žm­œ–A™>±¦žme/®EÙ o7+´¥$ËWÐu¦3TÁtáÎíC £(À‘° ‰Þøc8B«XbNŒõc1œæC>*m"uÖWFfâ$²‡ˆ})×:»„]µWy(ä1¤Dƒ gÑ6¯9_áò€ UåÊy'j4‰‘œªö•¸\ mV%JžªþWüIyK&@£¢]U&ˈ(#¯pöõTT„‰›¸&ª¥i%œ“3| Ø”&ï!«BÊÔ5ÃÂGÖò…>z_~´ÜÈcùAX[ñD_æŠäa†{®f 797àXðf@TeæEðÆîÉ@Ô ±åï£Aãpç¢ÂÑç@n ûÔ.taɌŬwT×:PqØq~ÐRâHŒÑÐØ·á|ÅõóXGøÀë kL—a¸ÏEy²¿yCèK]±m€+‹€ù™q®l,ä#d™BäÖI;Ým`ô…ˆk<{ F¡Øp”çMˆ¼C SÀq<{þ4ŸYâ5ՉБœš˜÷g±`’F,H•‰–†u’œ}’7+[Úºiiتí ð­Ç›R5F~‘i[fóЄCè³IïÈïá Í=¡ED¡WØõU ±ÂJæ÷9æÃ"MºÂiœfÒq¶ˆôhœåòf¢x6½¿eiX—qº ÏÂïÇòbZL¹¾iøÂÙ¹^¼‡Th¾…¸ÀÊ*ˆ#L+< Y^ ! ÂuGPq¾<£õŠ#µÜ.e}5’ˆ³j­ ÛY=„F³_˜FÊË1d&U¥>>1—®Â“‘Ùô+Bi9D u‚¦ˆ¬Ð9üê9²SˆJ*§ç¼F<" LUœ•D™‘]…ëä± Hëâ"T=o½W ÚhŽÊfšI8ip™€›Oô§•DXÏx{%ÇEȨ Œ䫞Ãã‰î—kÊa*È ¾ÄGîê¡*;C”‡­4ñ™rñT ¾ÑUµ‚ežçü\fйÚkjÞ”»)ñ4¶Û¼(I¦•rŽm‡“Å[…¢›Ãü‡ƒŠè¨^æùb§2Ðq_fÂåFS;/ªYa(DZñ~;£¶Î°kvÇ/iÄ~u”áÒqå¢ qÉÛ`&Ù‰•U@X/qa5L¥©†£l)c½!Ô–Á§œOPãÇv%qQ·i”S¬q« ”Hf°|LЙ¯8ƒ‚ËVZŒ2 Z«rPð²§@FpBS#"ªúÒsô-«á¥ÿôÚ•pøKJ( 3WÂmE%OñsÕ_‚Óíšj¡œÖF¤ÛPËÓ®h‚lœB@\|d,þóHh? žùÎtóf8‚&ârÞ Žôˆ.'’Ó⹊Ëá\~DñÖˆig½Ó®<—:ª´€*_Sí5ÊÖGR¥ä&ÅѶÀWó°j§iÑÖL²P¼‰»ù9HÒ×øãoR¬y®t7¥¢"rrÚ¨;Ð^ýɪLT \ÂQ8ÕÁ/ŠÁ&1·¶£2“9ò#ØŒh2_Ä‘·§ ãûÌWŒÆ ÊÀÔ­æþq 8…_aUˆ² åQ9®§†”*Úuâ¤_ã¾:%ªœg˦1¾å)ܶ¹LTrÓS ' Œø™žAå;–HÛ-?Ò©BxíÓCp)Ó‡Ò¹“ÌT%x…>4%èä’Êv[Ó‡Iœå« #‹0Yx½È£À臷„ç9”C7g†_¸É4ÁpÄDõ™)«g½,x6|KØŠâµé.½2pÐIج`«üÔ ß£¦I·Pû'§l~Š$,Ò"l(`áŸù:rPàt.!@s8"ªXM³1?'Ò‰0NhqŒ¶Va½p®;æì2Õƒx7®âÊt€váKOè&ékô©ãFŒ%ûµü'S§ð‘}Ž™Y8®kf@Sv*â[¸|h+¢‚ÑÂ<ÉÂBšoöÁJ:ïçUV¦ \Ü澂!#*L¡ ˆ/B'ƒèÜR 8­/À0OaõƒØƒ‡JÈEE_ŒqÞZ”Œu¹~ âêsLÖi‚–‰Ö' ãü€íÎö)À—̘đf|t0Õ…x}>*oE'ó¹k’Ë#Ê[FÁwšŽ¢?q´/´¢“‡˜|ÖšñnýH7^FÂ…Õ _Àž-ábULÃäÌ9O„§¿,OG[‚JØg>Åž|¤ YÉZÚ LVˆÒÉòø“¤ç¼øü©°sÙUºÂiùA6K®òë¿1×t"#-€¢ÜÈÊWU†[M˜ ær:Òý*€b¸E“€©¾gé%®%°‚W…„€”üê¹óTÊuƒ“r· ÊÚg^˜ÛŠ¿aèÉ@d.•Áäð3\ŒË•ËŽŽ- &Ml,Œ…ˆý_híýòbɽؑÑQÐ… D#Fê`fR¼ ;;Ö”db\k!F{…¸X? Ý´Ì,Èñ¾Tdz¯Ë]4àÒà’±FÃM@IïÇrf¸i§à/Î 'úñ{-6øYŽesïIY!höëB Téâ)œð1mGŸ½ñDzœ:(ôQÂ+D7Ø…ºÉŽ‹¯°bÄŸ[ùmnNV¢¬lëôÇ Có¨íÊŽòÌo9“gë0 7io<›+Œ´œ÷ò|ÑRòÇÈ-lÕÎŒ7åÚ–ªÜí³N™›¶Ú„À?ùdšMÂ$žl„ÅGßüá gkòÀl…3\Ny  ˆò‹)`~¨ÊŠ"#½)wâ¦ÃFF9u ŠZ„ƒ&«`|¢çÝ«‘=窾ƒß¬ÚsYâM>LãVáÿÈttÅÈ[Ýšùùšß7¥Jë0½7‘χì¤*ªÖ¨ø§TWQ ^º0„A&0l›åŸ¬Â)V!Í#—÷>ókÖgÉŽÖ5”Ö— x JQ¾›ž2¡¹ …R¸rV€Ò9ˆä¹…o¸ó¬Ó”{mV\ÁoŒ8ÄòV”Õæ:.ð®_t¨×‘\‹Ü\àt8z?·;ò!ü*‘7·ú1.|…†OLÄ/Ê Ä©ˆ,FÚqKL¬‘ˆ+æ÷ÎpKØ P3”™SœR©eˆ§Ó,i(ç+p!0U}SÕ@æÓ|À×JÆpä¯6gñÎåúl;l?ñ–k™N!ðH­+ñFBº.”Áð2μüU,åFëÛભx&œYXD0›Nø³Ó4­[¾4TÕ+ĸÇÑ™w%Ò,¿ÒVŠVmvԽȽí+/2š·ÒÚêZŒÊm’DZ Öµ¾±ÂìÉF @“¿W„;˜ ‹À¶Å{ ŒòÂò‹¼¡X´p¸¦-” Ö-=ü–å æVÈCøvŸ5ÝA*ìÙ*ˆgãD–JA?ǃèeBXИ_9œ¥XµŸ­£J7/í÷’þs¾*Þ]å*KÄ“3ª*WåOò•ú¨XË&+æœf\N%  '=D´r×€ùzF‰ià8Õ¾Ëë35Hâ­ØtmžTq%Mÿšîf@Ɉq£ëÚÚÉ ¡q“E5Íý‘‹[Œd?h‹0ùcÓshWá‘­,„”Š@)#RÁ¯p‡)± $ÎoMW³dE਎n'炚&‡ŒŽˆ‹’À/ÊçYŸ‡¥ï„ÉS€¹ [ü8•«Ù…¯Ja„pib;ªJ}ÍÛƒHÕ(;®Ží¼Y~¿¶ÎÍϦEFè¸Ï%0à"ßiÓüûT'®x>å\fš„‡IZ´ÏLÙÙ~˜&abªØœöü®Úse•¹/rlຌþ0l5¸2ÃužoEÓn^\ñعĭ58¬ÞÃÜlrÛŒ?¸å–ZfXábTÚÔü΂£‰‚Nö¦¦Ïñ; Uèj+@>ÓÒ \’£7çèB)àK•Ž÷Ú‡–G5û'Û_ÀxÏ€!|X •r0.¶CA0Rª€Äk¢Lœ¢ä:ÄuÈZè˜9­ëñ‡;2ö©%ÌoÇm>®jt°Xj»v+wñ!¨ƒ×zRîJaa «°¶¶šÎž:B®P°Ü’Gøú#«-® /Ë¢Xzl§Jw?½¾a‚ùQZ wY_Ðtuš£<噦›Œæ¶ÀÿhˆB_)‚ܰHˆ“ô’ ‚*‘,|àŸ-סî¦&¿2ÈŠ ‡%βȑ+Ï ~O=çj·êÜ UpS°¹ºË &e–ØŒ Šˆ”ár:<Éù düøæ?xyª]W` ¨dömðkIÜÎ>öeO]i^~ºÎ÷­(€À &zBÌ@¬áhcýŽš¾¦é,ÓÄ´Iý‰yʬDFÞàuµfŒxuFÌe+ Y…œJÃHíGçb•§ç†L š¿Hhúz£°ui!+›#VÅyŽkÇæ¸cP1ÅT  ›G!1Þ@ÓܹPѨ4zà"L¬'ðÜçö „£ëߟîrNßÎ…¸¹9·ù4µ<è2F”6¨tø¹ï°D´{TZ Ë;w¦»vb%ñƒ;Œà½t‰5ӭ׺ÄG®cÚ¯ŽòÍÅ?Fuh«Y/'f>ÒtBi•9|(yªÎÆÁ/Ñ_eTÏU +Ñ+Ñ–ø(TÒˆ‹rJ ñQ@õ„³~Û¾rg¸(§ ëY¶^|ùŸóãV8žýÂÅ.@N¾µZ2Ó]žgpE`"•Ož¯€{j N…U©N}I²n(é…‹˜ðE9›&‡ç5¯©­~ûnÚ°?Ûœ —;8„% ¼£­•Ǿ=‚ŒP;Ò{…6¿üeZ–fµ£n8—Qþ#¬uæu “gËõP‘ d±6@/+—U”€ÓY_<Ì»0›i´ .²èõY¹¸X×å×€½ pƒ¹ýÚÚzì¨ÜÊëbIô{›snuË ú ½ÊÎp~vÄ¥ˆ a'=ß(cºp&+¦:D?D•ä´š€Ó"‚Ž,UˆtêÑe¸ï Èá˾«² øêy:oFp*é®õ(=aVV¾åÜHà§Š)ÁÿáºSÝ”Ñ$ tþrqfŠ>¶ z¹Â373÷Oæ) W"õ ÙÀxBu~®3ŒØîðY× ý˜ÆØŸ]!ÖîÌ,V"SÎ Ö‰JåÒÇÑÁ 4ýÞIq¥Ë*éÈ‘×ú¾À:š[üó˜¤X£Œiëè!â¨K‹]ˆR€ãg—hŒ&FwÄ“‘[͙ۉ6À_©u!‹œ-tNíþ·J¥¢dÆŸ æ ¦7¯Lž¸Ù¨~õ6:ôXH”a(ÊD-Û$ …VásX#*,âÂJ@ðÅÁø¸ å`ýZ4¾æûõ(OxWúušõ*ç÷Þl¼²r)]"ÍuëPà×ù¸; 0ë»Eh\ÏSƒZRýlM™óxò @QÅ]éQÒæ‰ñ³˜’‘2$•íЦŽpPÀS)Óo¿èÏ*·~NÍßBTÜ O§WõdˆË¿©wÚ•§‚ Úˆê}¾–Ÿc·ÚW=+|üª–_1°«}iXb ÈÓ!F6EÈsœò&oIÃ!Bj]Á^² ¡î.§åÝ spè =v±\ôuqšèñt~;'¬B'¶n“Ö° `™4E,oI§PÞÞ!/3_êaÐ ʼ+ÆbQ£Ñ©è×­¤Ò ëú7£Jæ<ðj¹ÑÈëävo ´¬uÄÑ(æ‘Õ! ôä×™Óg1gÒ»ìÎv “š)@¬œÓR³•‘R‘…rk-s^÷ÎMŸ€ _¦¢Å¡{„iOÙDñaÞ‹äc±0„Xôè  ¦eAQ|ù <ÄWW¶ ‡ ^‹@Ü´ štŒ–‚–¾mpß¿Ž«#¼v°sv76Q|àAËbŠp‰ÿä*çIÞ•~ÿ‡ö8—¿´r1`Ö eHùÖÓ¦˜ÏÓVÛ¡ Sg|¦Â™´ Ú *3mʳ ‹i˜Ãæ°&óu‰ -ÃE8·ž«H2]5“„ìdGOæ¦!¦Ã!Q7Ȩ_™}Â߇épNŒæ«3šžé0!gJj_É©HµÆ!äŠ(J²4½cÌ©\k9UØ™VrR´É!±1ÛÅcäë}í¡Èe³&ïp÷6éoØBð>y¨Æm«ü2Šƒ¨¼ÙçJ'G~*§Ù*ùÏØ ‘tÇÀSÜ#IŸ_¾5w+ @$$U¢œäá.ÌMÞå©7fzO³õÙ˜m1Šåõ•óÒ#<æ8À²ßmiïþ}qœÕb£l‹+Ú¾!(›À⩾%H|EDÏð¾3ÄdŽ=xÏõ+<Žt.æÅÈf™ÐÆ9°Ýh'ê+ù€Ž´—ÄËñ oELáüÙmÂoç‡Àsžr«:€J¤8ð¬ Ïò«¾¶£Ñ8øº…8À¦smõŠÍ ¹,//è©,tkŒþ«X XPÞÖ¯ÅW0X¯€Ôc{%\¦WŽÏÖøER•îC8Y·õ•᪴-þ¦ÊSå-%dÐêIz‘>)ÞÄé‡Ë2EΜ½ JgA¢_Œ³NúaR·q×q•ˆ_žz•›œ‘(hÉkáNío?1+R•ª ¦nc«tñ¡µ»MNç:vsf¸½ÎZMÃ]Ÿ6ôçD&¯ÃÑU©9ãïM¶¼›ðýJ pi¼‚,ðcpâEyþ‚âËÏç äyЫ׼ÔM>@ø0mP†SfW»jàéÚ;mñ›0õ@ Tµ%ÂüSj´÷tÕÎÜøÜâ~߬(}&׎6{ýãÕþ;òe]ÔÑ¡í÷5‡Æqfq)owmÂØ2÷}ð+÷§¹KéðáÃi÷îÝñ"NÆÕδÍ'”cfÝ>£æ[\\GsâæÑ%”A£‚Ó4rÔ¡  zá†Ü 1<[2ú4”Bе4­D (àòp™ƒç°*ß×v}vñ-Âèú@(!Géy„_E@§Û–ΦSéÂÕ\¾«ú›Ý ¼pëë.Úš¾V€G‡Ý]XÇçØefNÊ,Ц0­dÏf¨ÙF"·´LË‚»z.Ç[œðºâDŽŠr*&‹p]˜ ç±ÐILq}¯ªM¤'/ÉLâÌVÏ5Š ‹.w ÂÄÙöèn¾ÄÇÁÃE?Yá=‹2uQÔ:Fñ#§üXo :íºÖ% ÑðÐÌÜž´k~œOûŽîO»–¤zg1½èÞ{Òçþúó鳟ø$¼Å™î)¦k«ô[Á+¬Qß:æ»Â'Ö'E}†os )&Lmćã* |S¥;päñVß|LIm'áI—ñ…0´Uk2ìÐ…›&Pumïf@䦒³?èvOpA0´®ãà ÄU~eXqr®c7,/ócS`£0˜;Ú`|?ó]MôåÝ»Òsn¿=¸í¶Øhw8dÃ:€‹ƒº0µ¸vÍHjKÇ>Ln%t699'±BÉçž\'LE#LÚ¢ŒÉŠ+û“Ñ¡Š§)¼jl[²BPØ¥|lSI›œŽ„©O8[ .¢8%¨Ù®5àV¡°Z- ;tc—„]h·òTlŒ(0æ:ÖÁ:‚¿†BpqÐ÷\;PÙ?|`¥ç–Àó,NvhªÌj.W¹“'˸­Ûrñ¤@Qr¼qn·é"\ù6ÇGâ´ÒÈñ] &E*xôÝDÈ}~7?_A—|–,‡ù04%èb?óäô Ž„æº£)kO-ÌóZã@Z˜[Nû–˜g3²/v–Òž;˜JÓÖ6/]Hç7øiµ¥v:rçÁt×·§sgKë+_N§N| €Âà£bo2X”id ó_¬†CD¤¼l <ò[Ù%ð7 TØÑoð¯iaö+i¬eÅxÄqu¡8ˆÏ@PÑ ô>†V¢¦ä >º2]ÏO7ø¾UÍYY¹xêÀâBŒd5Ìõ¼¸AØÆrDk±r &þûß÷÷Ók^ñŠôîw¿;}á _Ȧ-È(PÎùùuS`ëéüÀ¤?{ÿû1ùóM;³ó‹X½Ð°Ð#LmM~&üÀ;#+‚ ).—BOìyþ]!j @p‹Š¢.ãDÏ„)Og;zÉ8tE0Ž¢c§ÚÁW*ËüÅ™n›Ã³Â(!=ÍgÜ ^v®­ ; a6#yT *9@hkùõ=^–L,¼£—0eDe@ýá“1áÛÿ‚fø|MáøZuUy¿ä«"'0´5”ÍeÀ×~ˆVA×ÈeENËÉå;p¿RÀÉèû9pŒ;%‡íµ¡´?`.Íà v¹•Ìbð s¼¹YÛ™v°ËÒhîâ"ÔNZlϳ’Ž‚ M.=¿4ØL§/žI§Î<œškéK›«œÂdª‰•yöý§Ÿþ™ŸI/{ÁÒƒ ÓÝ?øƒaðcØ(ï¢s6¼ö³–`—êBȉ´¾ÜÛ¤(mÛòÜäó4X+S½ÉwuÖÂÒc±mÂs¤Mh ™U' ¾^ú|¾ª§$Ÿ¤k»[R2ž¨J®õßr"ÍØ8Wà70s=Æûž?{MÖÒßøÆôÖ·¾5=üðÃé#ùHzà&ØD£iø ?ü¡¥y8ñ{gi÷ž}ì“ÏŶ å×8K àk ¹OîˆïÜ-Llc@ Ñe+Í÷ÿ)žC5îF¸­ç¨Câd¢j´Ñû¹_l’ÉVZk[d|§å×]p±Ð÷ïõ-Ïv†°ò\gG!ˆ›y‰o6™$R†kú*}i§‚PjI”QÞ½§ –­rзæq{o"øäÏ#Ó’mŸpÀN\žŠ¹†˜’./_%ìÓåM ®ŠÑ£½Zd¥ qÍ–¦›¤@n”,1Y¾âƒ0»dŸ(ïC´xìd ôcîk‚EËcÌŸ¥] áh·–x9ª =]€Ë¿¤8¿°ôÅ-4éÒ*Û­'RÿÒ¯tkiñ“èðT«•§¦¡ì5©x†ãÝMæÝ;÷îK÷Þû­éìÙsi'ÓU§ ®Ù_v'*5°%àYø´tMÏ‚¡8O?WÂm­dù)„Ü«<*9€ò%cÛ?U> F$’ ™tûiµJ“³oÊÝ’ ò(˜•n~­*álÌ0ÍÍtDóVU !N¯7L§8ÚúÉ8~á _˜^úÒ—¦ú¡ŠÕý/}éKéƒü 0§bëL"{0Æí®¯vÿøKi‰)ÄÁÃGÒNÌ-¢É]5Ÿã„[„ž‹ƒX sã´Æ¶›k33¬!ÐI«k(!pSæëýXÓÔÎÇ‘óöŒ‹Aî"Ä"¢ †³¡JäOˆáVáÒÈg'ÅICF#÷”B{&¦/ÀG>h%½h}ùƒ‰)CBÙï€§Æ ?½ß¯‚ÓüòÂC.3 ._ÄMb¯•eTÀUú¾”1å—´h¿,CV.FþɃ¼0»áȦH`aAÁœÕ2€¤¬r~`ÐCuÊ䞺DTÞ‰öVÓS•¾‰àXóaO|à ì| ”`§=ॴ Ô°™Îy$±¿¥w’ÁÈ bìfÊÌÿáN¬Ö…Y}fû9ã*Ͳ@k™ºõçþ»Á¯oý®ïâ=˜uÖ–ᩬ¼íÃJà®òí[?º7í—x».oz喇†l#cïŽC!"Ý´Ú3O;‘†P5 ÏÜÒ)@‹º%|`Ë!•ÇaP^‘ÇðBƒ*@Žb.Ìùk44™ÎãgªT…÷/·|îsŸKŸýìgC_Á´àž{îI/{ÙËÒã?ž·ùU%¸®Ž¹ï¨»ÂÂáŠë˜vK;–Ó÷ÿ÷¥Óçϧ‹(‚%‚‚âêk  ´‡™ .® ¨ÜÝvó,>—^=OQaŠËâÀ ~sóë$l»"½uŽf£øµ3ÍáA2xÊÔKr8aID¦z UQ ùÅ/ ±²ª±åiL·Ÿ9’¾ò•¯Ä %´ýµoÏž°ÌÜý)qúÓ‚= *}:lY*8Mñðh¼Ý2b½B gàϺ«!¡qìV¡Ú®ì¶ŠºALx«Ìÿ¥XÈi©óFþÍ*€¨f‚76ÖOSáæÐ¼<Üi³‘ vÎËݳØS–wïIßùæ7¥ßûÝßeQ&Ÿ±÷¬û{ßûÞô§ú§é;¾ã;Ò+_ùÊĦ;ïº+ÞtÛØXKÇçÅL eïµÐØÎI/xþóÓ»~ç]éÛ^þ æmçâÜ|â@¢Båƒ.f¿Wfmâ»ÈèˆávÖ¿œ‚¥i©`I:Và™.´QZmî¨] ÔF¡ÙLžiȘ):˜:\ùg”§¾I[™„©éM EèÖ‚.œÎ6¸œ(\±bÇ‚ç•HÓŠ0] %”€‚Nþ|’Ïà–ðcÿ•˵nE_õ\áµqyh/t uÙñ¬p;Bël§J´(1Å[œ¹2댗’Ø â7k*˜6VÓ~älP›K˳ nYÀdçÖjT2žõ^™V“Ÿ2†ýõ´váQÊpÑ”w+€©yrŽúpÕê"GŸðkUV :â‡Ú Åj’ýLˆñöÉQŽ)9œÓÆìÕûV§§0]Ç9~üx:yòdº›)é ^ð‚ô™Ï|Æ’¢œiA7\â#P}9]ˆ).u–pá §xò‰Tœa€ræï@Äkt´^ã™uvüÌ×ìuæÖßÝØ ¡!œ;(7[²ÝÐݬ(…D«++xñåÂÌLm¾×\õšªxU•Þ˜g!ÏÆ¾öu¯KŸùôgÒ—¿üåx¶ /¸PËJG¿¿ú«¿J/~ñ‹Ã:øÑý'™½ÄþèG?pv®Î¹Ð2kwÝqGz”õ„÷±¸øÖ·½su@FÉåfE u n•ÊÁ—ƒµZžÈæ-7ÂØp¢6 Ð¢S¼Ž ¾ †”íÜ¦É èÈLˆÅ‘a]!÷ÒŠ=¡:)Øe±lUÆH¼‡…–rÅò‚1 žÃñ!üd3Lʯâö2ß(#J¼.áÌ_c]ò õJÃr‘Š/öËCP$é´Ñ‚»îÖж1¯Ÿ7Â#¹ÌÛi/riÉÎ}ìHk=i±ÀóBê¡Ø1âӌޱ^ÅçÂÝí­ÄΊmö±1b¯§nãùsA¼(“û³ŸüT”qì®;9u¸ÌÎbl0õc¼ÀŽtÍ`ž³Ô± ¨ 2qtñO&ö=ÿ–ƒò1d.~©ˆ4~u•Ž€©±$PÅŒPÔHƒƒþŽF<Úù!Ø2fšqN\,Ô…Ðä@V›;SwÝC ¬x=:òPi¦‡£n§ùÉ4b«´ˆ+pº‚«ô¦a•r‘Œ&ºÍ„ÃjòçÅ'µ môŽ}Gv_ÿ¨³¦î_ÜÅ´l'Jwë2K˜Ç;Ò#w¤yÌf–ÓÑ#0Ÿ?Jß|:z¬ó°oÞï^¢ž¶nU¸”eR7¦|œjÕ®ÇÇKs$«. Y„&aûÖ-V‘7½ÀM wÄšÆÇpË–À–ð–x'ùÑiêŸüÉŸ¤n_ò\†ŸŸú©ŸJïxÇ;bàŠõ‰¨q«L•‚é'8p ‡W¹Ývð¶tû±cÈE6ýoƒïÏŸ»˜>úñ¥|äc°—¸(7™g|Ý]ÅùùúÄZ í˜ðõW=©\Ýò)@Ñ¿%PUÌMZÌhûMŽ{é¶‘'ôdâBæm9qÑ&Ø{¥¯6v¥ù(¬+÷kA¨{ï½7}âŸH𦳠‡/yÉKÒ÷~ï÷²"{ohà³gφU`¹:5ê»çΞI_ýâ S„½i÷¾=éà!´x»7ððë©*ïã»8ˆ°Ï‚óÚ* D¬H\÷î-Ü許¸ÈóÝÞVÄLì<(Ôaþ†ô0Â#´€fÆBCLöÏQHut–¦¾²æÈN_g[\lTëvÿ_nõ9h~¤F>"&þD~«¸\æU BQŽ: LjJÙ. Ça eòê¶søZc1-0¢·;ó,²îM{`ä¥Ýsô4èÎr£0¬™œ\í¦KL•îÜ»¾ýÛžŸnÛ³+8{>휻˜¾ÿ}è˜ xÔÕŸ³ìX?hÔP&è!]ŠâCVa³"Èí™tÓÊs+q%OòéòŠrV T÷çþçc€ÑüW˜Ïõ€×¼æ5éç~îçÒ¯ýÚ¿‹>r:i>Óà=Ly9ëS{X/ØÉú—¼ÿÅ/~1^ÛÜì3À=ÈΙtáâ¹Xßxâä)”K7x8[Šò |¥å‚uëKtô¸ììÃüÌCôfðM^dÂþÆß·¢J½ª(ÀãM»á›ÌÀîŸ:ò‰ƒ&¬&¾#¬æ“®m<ð¥à»¢/K 9r5 Ù¹hègïÞ½!ü¯c:ñ:¬§<ð@Àh h²éœ¿©©Ïž>™ÎŸ=îÿüçÓ[¾ë­éµ¯~MúÄ_*,„ÈkÖëÇ½Ç „àK§2ι6Y‰¦9¹\橬{ÇÖuùzo\"*##Ôq¨–‚Ó^F¥Gx¡HhðÜ7EPc!QPÁÓŠ0>ȼ.`À'ž&][*ÁÎ| KøFà ¸£·L∩"ñ¾¡Í¬ªËNõ=i†vufv³p;—ví˜gîíÑ×Ù´6ôÅ–‹@uÒÊêÅ´qâkéô©z:Ï» ñzZÝèQ-7E­\HGvϤ}oz &á0â×ß{Û÷¤ÿôûÿ_ŒþžŒ“&n›Š^0r™²–ƒ2â­0™:-°Æ—gýò1^W„¿ø¦—pñ…3^éË7eAOÞTàý~_ú¥_ Óåã±DÿøßBXL?û³?¼ë¶±SRËrJëÚÓ×u&WVÖÓþç?Á ^‰ø6‡víZB),¥{¿õx©–öí½=½—ó/Ÿþü—QŽ;P&öaîz¹Ð­ÌPˆ >³G·Ä?ÈçWÃ5),dÛV>=¹»PJãÐA^8}èÈaêg8Á·²Êü†Ñ±3‹¹#d,¾E@5‹Ü¾“H«h]…ÕEÁ»î¼3=Ê@Aßæ|ôñ'¢óì8;ë"§.¸p¨’ÐD{Ñ‹^”~ò'2Êuz B°ùÊ:ƒfüK^ôÂôÎý¯£cçè°yp‰=gÒs âsîÈ-ò˜ýt†ë-ç¤Xssój=`ÿâ“S"¬§2¸?Dâ~­ *¸êPð,ß®#ˆðå)„,nMFæ|MóØî«…ß"Wä-ù-&œùJX¿R"5V•]Ñ:êpž½Ó^N9¬3^@™å4ÜæhÑy\ê°n3[;6VΤ•á%Ž—q· ÷KÔ˜ï¼îNŠ·Þʄαãà qKï¾p¶—öíÛ¥e7"8 X3m¦ƒÊ4“®P?°œLÃÒIWü’^žM+qÅ7m:< ;]–a^Ø"ôòËyv•̳oß¾ôÃ?üÃé-où®ô®wýNL;wqYË&Bí5n à>ð¾°>ö±Ñ ô-qk«ùb\Bé´›éÐ×ÄÝõÜ;ÓþýGƒFKGÒc¬¦m¦¼wœÞÿþ¯¤S'Ks»?žv˜ãÄ"<ÒÈB(S¢><<@ŸJ-T}žX˜6ÇïFÖY`ïs„¼L¾Ž  b>ùŠ×VOÚ_ãÁ¨æˆ¬i;â6G™Xq-`ŽÓXš³ÝÅ”¢lˆÎQXaÞÏ.€¦ŽóÆ×¼2õWO¦ÿ‡•د²Ežxâ‰é]<|ÃÞ¾û»¿;Ãý÷ßÛ6já²Î`ùv²&ÿû#oOÿìçßî«éŽç>—õ‘`âr%&/5Ò‰Ù:ˆ[ƒÙ¾jÏðbq.ˆ…Ù‡ïöM‹UªqËŽQÖ2Ýõ†Z’©*ùƒòt%Z °>íd¤xÝ×¶ªXô… ­¤áœ9{Ó™¯¦rëwe×îoaîs‚\[Iä7Æc^:b¿´¾ÂTÅ ¾—Nù†Ú*BaÄôðÇN[p§Ç–…ã?ð³Ï*ÐäÝÙyÎs¬ÄúÌoüÆo¤cÇŽ¥W¿úÕ±¤U‘­¿l*SH”Q„µpiŠñ%îJß´+?æ“^~„/fùô‚œyxi­‚R¸^ôÚ×¾.Wçë.œKÿê_ýK¶—7Ó}è‰zÚ î³«5Ëö$g~èq™¹Ð=G>ϯ´ÒÑÃßÏ:-”éxwúê—O§'§÷¾û|úëO>ĮէÓCkg(ºrh“2ðÝîôâý(Vߟ±¯± fØ•êù&!ŠƒL¼³€bP!\é°*Ǭiñrá`­Ûíû†®®?Ç^çûÖ,ˆh“=!ÁÙ$Cu³‡‹°Ë˜¤ó¬ñò‹7ýœd޳á÷å÷üÍ[œáÇ?žö0gWÛž;1ýÈýƒôª—¿2½çèóÒ™ þÉÖÔÁ…B åÓ†OúÓa¶i¸Êêâá}÷Ý Â4OzÐHç‚¢; »—9PDGõGÞ7¼ñÔsž¬§ýöY [­8H4Ï(éÔ F2Á¦²°è­Äš½~”Íâœãa@»Î² âà?ÔCŤfšd#l›$‹JDZŽSžBˆ'À%Ót¥¹†PŒe¾à£¬Ë7Íû曼°²Î•äœ~óH28h˜bµÈlZŽsXKŒ?¹:wRœwVf©B+Þ¹MY@í?Ù(p¾¡é|ùñÓ>˜:+Ü·±h{†ËPí³ÜNÊÁ™·wy6îÊOI»–/ÿô‹8(ðÆ©ü­« šðÂ)ðšðšø®/=zËò<ô#òŸæ NÎ$ 9t4@AÖÄõ ¬v9öí^J;–›iqy.9ü&¶ÝØß`·ãÜÙék_~4}üñ}é¯>t>=üÈZzdõ¡t¡º´¹àfÈ©Öá…t˜ÃJÍÚZ2p 8„4¬/°†õtð™æ>V'ëTq$]þ!7ìk5ªáhmôA³ÆAGÿè’é$«ìM|ÝšÈFhÓÇ¡2DÇRd$çØm­ã-¹ÔïÞ¥fߎ…ÙtâÔ™ø=z€ÎŽÐÙùvŒVÀ»ÞõÿÆÖÊýØóI˜OÇþBúo~ð{Ò¯ü»ßFãº&GPÉllÿiÛãÅZ®|ˆcÄøÀbÏV%`Úë_ÿú˜>¸áçSŸúg0Ÿ{˜ùnÞŠ¤°ô”ÖÅ^Òv3 Ù³w?õ†ô’Ì­>Ìs•ŠxÌðb’¯ +¨.*.²Ž%>…¹m]á£Ád¡Ò¯âBÈ%D‡eZ–á1ÊÅ©½/Ô–3ßUØ:}.¾éž=¦'¢.¥‚í SZ€)ôäŽ=5Pß?£üÖ4·¿òêtPk3>NQšö¯‹hŽô°èìwYœú9Úú‘ö{áýi%0–¦+Ô®ä-ñ–)œ#»ÓI^+r/ÓH/d¹—Ì<—rÛ¡ƒÌ·÷€ßl(€Ç{œcé±Êÿžtü1.beÍb^]Z„.i9¼„²ìOûÝ‘ŽâxqiÌÁ{Ó>²N¯¥O}¢‘þòc)]„/¾ÆŽF¸?7¾Ä³“P“5¯Ö®t¤³Êa5Ðf&WÐ¥™.&,¡ƒZ÷xg¡ÉÔÌ)jXG(/ň ÊŸA õÍG‘sz[zYºûTh Î@ß37Ã×_Øýnïg´µÛò¡ïHmæBX‹QÒßÝóž{ç÷‹Œº0·|;33D#¶=sý?þß1UØ›þÏßøõôÒû^‘ @F+Nea{Áˆ' _õªW…Ðkº‰“ åó,üùFË@epÇw¬Ç‰<Å «“‰ŒWœe48}òæò—`žC\Ú¹eà¼Ö7üòâ¦+¶2½;³,ì(ìÓ«üîí‹‹.ÄHâøˆ/³êB àmoe¥ÓÎŽ¦||Î@|eUYƒëª"<ù‚%:u.íº>áù†÷H¶øÇUø‚wBÛøojn“é6rMÚÚºVã4Ï>²ûÀ° ®Å¦€–QºÐÈrt%Þþ0¬o ·N^±Ï ~ ¼}áÈï~º}ü|Š©„^øÂ°Ò~š£0N˜'N1|.Ó™Ó'T{Ò+_}'ÖI#ü )ýqóé…£ŸA)@ãöë)«ž0xÏÁ^œ7©•ݳI;Vy?¡öÿ³öPr\×÷­žîéž<ƒy0d FQ¤ÄL‘–(ʤd­Ã®’m­¼ë´²w}>Ÿãݵe¶÷³|öÈ–µ’%¯­`YÉ”(‰Å &0‚‘I˜`rî™î®ï÷¿U5Ó‚0Ý]U¯^½zïæ{ß}Á2$&TF$­B›S!`]’ÍÖÐÑN ”–£Z`Ë*Œ³Å!23+`3È‹lƒËtŸ¤/MhÞ:-õkÍÏÎÄ@‡âlå Âá¹ËE©š [ ázkÀKÌ ïÁJ"EñªY°!ñk„IÑ®62}üw~Xž³OüÅß"ŠÕ0¨ @éŽ*BîJÔ‡CûÛþŽ!ÛžÖÛÿ@#." G±ý 0€$n"1~õÊvïm7ØÑ£GøÔžþ©/Î#qT@ÕÑÑÁýzï{ßëvÕ•1±³³ÓZÏHT'ÓXϬª«·5tÔB rUu%C} ¾Äø­ö’¢ßºDg¼®®Çut=©¯û}ãkñ„.ÔõT?i¼ü[\9–ÊOë·hG€W"§*•ð¨ÂA«k×ÙË/Ž£^VÚ÷¿—¶÷˜–[GNí½PÈ7ãTW²•ý7Ë9 "W6“´uµ¥šÓ6ÚÚTü™¡Ã>Jmù5p³'œ=r³–½^IGJRË}›»œ1É!5Žz½‘$€ TæÃ¹„Ñ»™†YFsFØçH?ù™CTe@ÆcˆàÄíqä—Œ™Dyð—ú‰CÔ¨”GÖF¡›ò(:Ã+”S«ñ^ˆ·¡¯Êê3°¬¾¬Þ],Û˜€\„*¢ô ˜P™€*£[°XDc8doÛÕnþà) 3èáÀŠ2°éÅi”DSpíÕ[íÞŸú]ëìé¶/í{îÇŸmÞhü!N¥Ønq«¯~õ«W I2þÜÏýœ« R+žcõb"v Øàî¦ɧñ}‘ÿ›s5 $3i[˜KÖ#žáÈ\öÀ½¢ó F„èe_ŽüR„ðšZŸ^Ÿ\‘Ñ4լΫ? ÁHÎ ñýº¯œ «€•9Z3µ¥:j3) ¢ë¸œ0èwR/éw¢Û‹ëÊð'„VäœT,!¶8·Ü³âÎã{î¹Ç o" "Æz–_s¨E]BnIëp-믫»ÓŽ9æ†\ÍÇðШ/ë K,ÖxÕÕU»¿ãò5¶iÓÝÌ›RÒ5âõ©³ƒû»l÷Óuöô³ãv´;g'ÁAVá,|€8Ž5VY·ÂšÓ,C TV"Û¹ê ¤=³ž[ƒ‡pÈí1RvsÀ „E×5H%2qyb·²†[û‡ù¾ñQä× Df[8Œ‡Àõ [Xº›ç`lƒ>ÎÄ[kNоR‘Û} ²kEbãsʱ4Â=6*>ox}™9)ú÷|’çó}¡@O…ú6¾¸x ½z0TÊaXRBPìäWD òÛ5£G¿òâ«v÷ío·ÚpÀžxö ]síöòKû¼ÝɨœZíŠ5þûÅ—Øu×_ã¿4Њ0Woo_œ2«h;v0Òa÷ýÔ»œòܳ8X ° %%$⾈AOO‡oݺÕø®»îrŽuüøqÓ:E%¶¬@ǾñáÓÖÃsÚ7nð3½¯â䎤:Ž‘Kêœ×`cnTqÔWݸ¨¥È3 ž”äë ù“[Ôd¡€.Ɇ¥Åþ|šˆkðµê¥Ñõ~gþé™:§wV]ŸWÒšÄýüãîÆ—×X‹“K:PÝóíoÛã7®¿þoç QqíÛýÞmÛ¶¸JÐÒ²’¨Í(Äû‰ÇŸBë±Aô÷ññI=ÏÌ¡ê-'OÄ ²Gµ"À•1Àµ´¬µƒp·…ßÞ[a=>@xí);> ÃHá¾K#š#v§á¤UÅA[;ßcéùn6vi²|e ::iA,±ALv%Lèç2¶¦ ´·S8D­³{x$ã½4nüIw2 CLíc3 ò„܇mþ4zÿI¾;!PŽ EÅ:®1ðº™F´ÑMŠíë %­žŒ$4¤ˆG0Èà|f‰%åÿÎñ ¯óÆŠgÞxÆñ‚À†Wçgê —…<É‘A|è@!€½Ëëàè¤[Y{ÆC;¶ÿ =øŽ›œ¨µa‡*@EÙp€ µÝýz‡ÝýSïðóy X*Za&Žó¹Ï}Öíù<+Æ& öõ/Ýn}ë6ÚiÄ¥ÃòaðE÷DËsÅ¡¤è[zh9bËþ —¡‘´êKÆC…èY2(Êà ±5)¾XQM^‰lâª^˜ GR!LŒ8ŽP\ÔÌèZ‚ü:£át ãä׸ίí3©C ’Ÿò3KIô¢Ñyj--PƒVÖ๠ÚJHRÚ¯1`‰Ÿj<"êgÒ‡džõ­±J?±Áˆ#‹hÞÿý>ù—éâ~Y¢¬ÒÑÕ²ÀD÷+6d÷îgÝþ£çKUàªí~þEâ ô5Kȶ¦–å ¨-¶e{ b;IŸ.Ȫh³}¯³£¯‡öÒ˽¸€§mhФ³3À>œØæ‡þH “]g«?!Ìò(Í#ŽØÆmž…FÙV¶s»Šq˜Wû¡ƒzݬqÕ„#±àjåzD'çú F»1@SY‡“!×ÚcÁôë< ‚C›Ï’ à™+W!6l`þqý!ú‡èþáÁÒÙ O0Š3}äAHÐò¼8qa–´ÂaFp£®ÆÖO'¬]éUÐè£kçõyA i€ðŒ Ÿ^ë &CG9%€Á¬L¶óüibÝM„K :&ùÃçìÿãG¼)஽B¡ÝG 'Í#kmÚ¸‰òC¶å2\:Ë02 +‚ƒ¨,Ƙ¥5ÚDS£õÊþ#Öºa½íÚy•}÷Ñ'ð­’F n¤"®” )d9*¹„úAzõMÖÏ]H]!¹ ‡O=õ”ëµÊW bpÓM7¹ú u úÓ’]/>±Ìï—pUMP2;ªã&Ä÷’!¼~ ø“sÑwtðCdOm%U’oÕôßÅ™%‚}ÊC!®Â®C%-yÎÒ ú0œY¾ûɤW;âÚú“´“¨ÒË…øoOùеPFzÿ /¼èqñBrýé]ô­Œ¶2\)'„<´Â9=“®s .ƒ¾X ·µmÁ=›éWÂÍ×x€VcãzŒºUvxÿúz3KÇGíDßõOî¶¡y¶½sQ»8Bh®ÐmD°íú?"w>Xe%–çAÜ ÞYᵩJäÔ8HÔ·"j&byˆN®²0þ\Óx—îCbÚ "–‚Ä|Ÿ²~)ýf-cÄðŽw+M‚ă¨Œu¶•†ø†°„R¬:†X¾÷ ’¬ÿ²È8ˆ±Ï‚:ƺ‰×Ñ9…d ÁE“"£¶ìy+‘lµW^8éS02–±NåPµ)gT*¿áì¿/ŠÐ”÷“È/€bä…sž„. 5 º"ÑZˆÖ~éz{ö…}öÿþJEƒ¿d”eWeb‚|xÅZÛ²m£8××ÃÊJ9"2žoœIPèBûe÷¾ágôª~ZáÈÌÃ,¬+/·\,ê(áH!½Þ[ÄRÜ]j–~‹È¢/kþ•W^‰}‹/i=zìˆ}ýë_C ˜ ®bÀ+bÛ҆ÂÄ#—´Rq¤Ìâ³z­V †¶¾õ `b9D?@‡ßb{÷Ê·ÇzÒöÄsD"Ñ›=GDæfJºÄbÚ^›%A"yanУåp¼ÙDÅj+¤¯@”®!?Œ‚wWÒjî‘^I]W¨<1¬\ •jqD PÊK€ý®¸–jÌ£—äB@³>¯¸TLµ}¹ €!~ü@>ÛW‡›ó,©6µ)dçœÄ~#Y¨‰ØH2à]|ë>¤ešÁ˜æJª_…è} ïãÙÀ *]æzÄ@ʦU×Ýÿ\˜#N;, Ǫ\R%ùææ7/KcYÃÞ+€$#@Å,Á | DÏaµUd˜‚xª‰¦“û,œ‘6(잃þ¸©}=Þc?ýÓ?m áU‰,ù“îrÓñôԘݸs£½òò>rµ±¤ öëYî;0pÒ¥•Xå5á{övÙæË¯Ðm‡ù‡ú§E’d™–Ôpù•;í§ê›Ÿµ?øŸŸ¤Ÿ°(ŠÄö… RȘ%±UÈ ´fZ‡ðÑ~Ô ŠÇ±#xàˆ– 'Í8«MsQ['ýDL¤u(ê gú…èPm!ü%Ñ•øÞ¤^\ý _ Š8¤.é9<¨Õ ¾ºyT€¹!¼3Hpi=Wã‰çR8~^Žv楩©ãÚjGÜ7ú;(Zîå—wÛž×ö@äÉˀݦ2SšÄîEHm z‘ߺU—Ùì2¶êlוkÈØ¢7·@¸Ûm?jõÔ-³}Ç‘¸ æ'Bv5 ?ŸB…„+ÎvX3H]©‰`QR!¬†ó–0&C`DXªŠðq9 2eœÓvìR…AZª4s„÷W# «Ãì:|fXéáþ©ùG0OV溞oÿ 5à ÔÙËaL’†È´‰7Àn×÷°˜ùW§²Pwæ~¦ë~:?œžöÂÚ«xÞzïG Õ#œa±ïj-tí2¼ ˜­~û#ØÑœÃ<µÕ#åàH„d€{x¼¦+Þ;_ñÙ- VÀ;ã^iÒ/¨\à0y:¤‘fÀV×åç´øC/"w„ûȵSèµ~«Íר±Þ“Xïw8hßÐæª^:ô*[d'ïÚ2Ú,>7Ýp…ýÍß·[/T¢¥½"Ò³2lÜÐX—³£=½v±ÏÔ•!}Ù1-Qv„4c º¸á–Û¬ûÅãO?gÏ¿4ª~l7ðߌxrL]½Ÿ<ò~æG"•xT9ý%ZGª€zÀ½‚(X‚ä:¤@¶ù„Êë€éòÓìFÿu–BXäó>¨Õ)/Þ~tÂÛH®Å÷éPO’p¤­aÙ¼êqãé,k#$ª+:rÆvôù-1²or¯ÆÑ£‘Ûóرãöò+/£ºMXs¦TðŠ¦«ª"6Œôõu­•¶ñ½yU„aÎvli \¶ºóºg6Ù^ºÖº¾^°®`8„0‹ÃI5‘½(G$ÜJ"XÉ´ee戕*/s{Å8Ñtá+´Ý}þ„U̵jŒxrÍ)m–Í#lNëCëàâîçE‡C—ÐË¡ O$\>ÀHàâ PG"iOZˆ7C\™…Èü§”]yý/³yqoõÉÕˆü+ ’u¡è"Ü]ó—k¯Åư9z>¢~0×IsôU†@T I]²M¤ôn0H×óiTªô޹¢Mz"j(Ñ–K-"j1«øo-˜&/gfyõì¼Ó«• %ÜÆ23›YžF™A¬™™Î㓬´2DzÇ(t™KÖd1BmÙ~9•!Þ~…c¶}ë®-nµ×‹i5™ŠÞK» W×E^GŸ9h?õ®wúµ9DF• †<¹ T4ñ³#„pV#Ò±NoßÞn¯8Î@+VA"—F†,Ýs’•Z8âáÿñse·ÜúvûÑ3ûla°ÀG@‘ ¾ê«$*‚®éOÇZã­`¢Ûqo©=—ht‡úºˆ°I{oý—T[¬Ï ÕÄB‡rµýÆ’ÀäB@5‘ù‘ÕçêÆ¶>ƒQéºën€ûç=ï tòÄȳòÍo~Ž>qŒuucl‰Ç“ó–·^‘À»z#R ¨ÈÀ›Ëm³—÷TZ7ÄþGíYû¥†/Ù²ÒípmIeý6Zú=ûVõ–Ý_°z¸éj˜„T’R «­‘¢p£øAf¹¥ëvúËÉ0WÌ!]Á]|ìò– Ü%.%„Ð.Ø!¾9¸j(D…gZå „G0Dÿ÷[àz4@~<R„»¿ž.Eï1£þL¤oK“¶Ì:ÌÖN6㸌~òÜŠ-Vœ¼Ù ;Ÿ²E•M«?3€›;±Î­';Ýz.ób {ª€naRs‚oú,\ ©ã%©H£*¨¼—r%H-l/NsŸçŽ^3Î)≦:c§ŒŠìT7?/&¬Â¡×Ò QãüÊ…o•—òŒŒŒÓñ@[©€7Ä5Ò÷d¬K±¨"Z‚ZS=Ô‘ ‰½{¬}Ýr{ü…ƒöÐ{ï÷¶Æ1Â)—^VËG£â ,ðìW?ò_’î¯"£T8$=U_ 8:qrÐ^96emë78H› ®ôxé¶ZR! $}ÕÔp¯}õÚÿúó?²m[Zíà¡ ²úÃÎøÐµäzB #M´æ-2|&碑ґæE3(¢¤ã7Ÿ¹˜³k’½ZRY«$ÇÑѧ‹ÿEK…ú@ˆ ü|?Ëc„ÝLÜÃŽ¶µ6:!$ïCÎÞï«%s9ó ¬‚@¿å¦í¸ßZP6ÛÒBSÓJô¬°ý¯§1†ÕÙ7ë–{Á©a^¿Ð ‚­6ûO»0£;Ç\ËÓûyþfÛÄõ©ëɰ‹ :3½ â¦-EÃU"Ê[¢ÃKW1š…åAØPÖ}‰ïµ×1| ùå%*ƒLH®WKBpÃÈ÷Æú·‹€ËºÎ_µÕBYãQRWD,T¤d#QHpJMÐnKÚV¥Áv;†`ðAm¿ÙÏÀ½'¨tPx¦ÑqçN°3Ðs !REiCˆOú ò'"å¹sh;ƒŸQ‹~x,¤â¤˜oEÑiE¦Gª;´/)-× ^å¦pÐñÆùí°€Õ…1Á^Ã`y‰ÉC|tž_Eh;œ¤è'ƒ¹êÊUlV"{ NŠ4¥Ô`¢R)ÚÕ;wØ7l´ç_>bÖ·Ú÷yÞ~—J_߀nj뷶‚˵œŒ>‡Žv±<²çjµCGºIv ~‡uvvª:»èLÚ QaâÞSìæR Õ¿õÆmöíïþÐóÉ–^D¤h=¿¶èjÀ(¹ïH¿}÷GÏÚ¯õ ±Xà6*N~>Ex(d—;Q°é´÷MoÔ@qq»AüÛ ‹~«Q9X¨¦Ú~Qù±ŸŽÎú™xqøêË­”mƒÑ6»dÂS<+DPÚm!¼ì,©\¿…Ð~ÊR·óã]¼žQ? <_ñ0n¤ßšŸª v¤¤ï‹ÓOÅ qþ<ñ?æö´. DãPI¤€Dä_T ˜d±/ôMÝã~IšMŸQ¿ÆüÃÏúúÐ-*ÉÙ„óGgu!i_0 Á-ç ¸èj톛n´ë0ÄJšªÊÕðèZÛýìqÛóB-H?Žo½×Ʀ:­cd‘Xô†{£UV¯a‘L£µð‚rU±fÅÿ@9âwþßUØ:†=S< 8§oL,燂˭)zÙ±Ç!\ƒI§õ-$ÉnˆÞGÀ.¾z+C)LC_—»lö(2 ¡ÀÆ5ÛÉyÞ c!l$ÔIŽtNH$è7mºŽ­@£+÷œÛàÊ¡âõ!ÒÅõF^4l£´ë\CRv‚PE´5@IDAT‚òx ¾›Kc §*«ÿÑê FEõMêÑ…r/†·ŠZ¸}6ˆa ¦_ãý!›ôJT1Œæiæ^6E6ÊÁ¦$÷ˆÀΣHÞA‰ãœÞY¶ …z ‹á}\D¹ 1Ð(-ïŠZä¯I))–¢õr7PpGãáVËïåçh¤’,¾{ÜKÄãp^Ï1„KH~õÀœÛcF6¤ˆ)À‰„ÐN_2bsð$ã(/N7ðRO1ö"f±Ôó<ÁD,®òó2OKàñy“êÁsT y½‡ }H)Ž:ÞCÑ€§ Dx8Ö&'ZÙ'U¡Â‘Ÿ÷'ÔW @Kù¸—CMç<óî]x°z­NDA@ÄÜ8 Sï"Pèêy–‹!ŽP´ïhttäTÉ<Ð…ÃJÂŽ'1j €:ª-—ÇF‡ìë_ýŠýÜûÿ}ä#²ÿÞØæöͶyí*;Mü ùã¿‘™`,øJÐÙL›*ýÝÝvÅŽþ[ÖvéÜÚpD%Ã:íöÐ3W°•Ó+/>eÛZk ª#v€7ì‘ßCq~•Þ“Äð¯Ä€GŸÜkwÜó¯¶ñ¾Ðâ6a«ÏIè ¦«pÙ€kÉ%?âïätù³“sK¾©ŸÀ_y]ÿ-=¤ŒûG×é3Óf¸Éÿýô~;>hUµ€ÍVWM®Er$ÊPèÑrs¬'[ÍX‘àqH\V8ªôn­"$m¾¿§ºíýŸ¾ hƒK­‚¶Ó‡ÕHùÊ”~höàû]¯þ„{©¿¤¨m¥ÅúÁ.²øû*l¶z­… oóê z÷å_—4 ß>Èì>ˆAHV a .ß}rÅIç—t {g»øb91DGA[®†D“¶Ð¥ž=ÂP € ¶O‘1©lµ££Ä³ îÈP¸`›Q¶÷ØØ¹ÈêÅ*V'ò JÉ~¡X$˜Pï£clˆZb•ë °„ï#`?ÃØˆ TP¨9UhAS©"b¯LÁÉÎIÑY*8`a'… cë‹Ï_8ðrãE€…Žð¤<%N;?ÊŸ&W`ˆ*Yáy^I+Ñ” áÀC’^·?ó´Ýûö+í¿üÎoÙ7¾·Ûõèúzå_#ÛDD>9RL©¼p Ûîk‹@ω^Œ~ã¾"L×r´™%Î]’6CDNÛ±>m€¼ózXö'ˆHI\yssì W¨eAQD`xÏDÎÚÚÚíx@ˆ˜•õŒs•K žƒÅšŽ þTâ/?†V--ªýÒÜ—WѽåǪª¸‚Åb¡¼ù£¶Ä´3L+›WÛ†<;4ã)t×òÊtåf¢Y¯å¦V òT×9°DjþäÖŒrÕ< £™÷Žx;{Cþ:éÂV££{Ý’ _41$Ë ¥rk'†¤Ž” C\Þ¹¤¢2yF®GÈRè_’»~-ÄöÀ HA5 žDø±;”Î+;A D*¡ˆ¸ù¨ÊNÒ—f»ÓÓÏ‹Õ *ÈéꆫQ¿B6µµÒÉ÷s$ÆmG…MÖ£¾ „”0mødÆSæ~êU.Ìð’!0WAA‘C%Ù%d¸!PÂÔ–`¾ŸqÄ\Á=¬T7þ1^ž½_s¦øÇC„Ë@$ê(Ÿ¨)6Pa›LÅB{9LâÓoþÓš7¯tŽ«þ@t 0pRƵ9'ÇÓèþr1)1¨„F-Ý$‘(k°k<¨æOÿê“625aM¤iÒ&Œï¾ë íx‚I"÷´vÿšk®·‡Ùêår£À½± (ö^q*YOyø Á=µµL,Ó§Fl;FCE'ªHçÖ}»1ML3!¨}''ìùÝGðgGÀš„ÀF5Ïýé/Ëeíb“´]>Q A\4LÑÜèzr_ruJ”Õ^¼ à¼ˆ€þ’R~=9w¶o·0žU5ä¢&ÿSÓý,€iÅyk®´J¸lUÍÕHSHXÑSãOZ8ô°…#ßgዬòˆä¬i—xîâµH”w"$£w?¡³hÖq–(sˆ¥T±É @éñhQ‹ŸÔ‡ß/ŽG[ lXµÍ‘ÒËdvß¹M€³€ }^Æ<èQ»2´!ö‡²L<‡ñðUDl’Ⱥ¸NH­,úÏAö’Œp3ǸÆ1ú¹Õ^h¿ÖRù. ÆŸEÚó”˜u p¥?,1 ÆY R®[I…wBò¡M ÂO©.VG›.ݨxÄEÆËPDA*ˆ¡T“4†MˆO˜†Pd×á PV y:RÁ‘¼`Œ+\"¤3RŸ¥€EÕ£ÂüËŸ ŸŸD…ºp"Â3+Ç÷œëëR$‡jÖ×÷éÙˆÂ3HJ ¦—âRZy–ñ ‰ œ´‡`…ånÛ¼‰5ö–í³Ï~ö3lv­Ýuçíx ®¶lí ›Â5¸œ ³÷ßÿNûÄ'þ»-Czo$÷–ˆ‹–×á®R©%"°¯·ƒÔ)Û¸¡5:G†•ëv¬µâh–ÝhšIï¥5éÚˆ4)r=ŽŒN‘Èq%Y€ Tƒv×-;Xðc‚˜ÈÇ4¹óßZôäF¼ûeÝ÷…>KÀ°Sް>hg4ãωï—GR‚pD÷éÛŸq+Æ£³µÕ¢)ç&'m•„2Kgª2M &4é«Ó{_©>Co¶,†,³`,K 9Åay†tt°/j8~!Ávé8g·°s gŽûÂPGOç¬ ÃGb9ò78Áãü¥’A·dÑŸéå<ýáyó°r¹×“¨^¢oÖ{_ð#–è–w±I*¥cœ§Ip %åPlG(ƒ!£¨¨<^»@åZæ&£n‚<ÜÂ@Ö‡V·Ö¬•ä]*Û˜•X¸c•ýHØÎ ÃÃŽ íbƒÐª:wÇÑf$eh<ªæºÜp(0H4,b<€¬¸ºbàô w{Q3Ä Fz{ê¹Ê Q—¤ÌŸ AÍ.¤ÁFˆj<ÒÕõ66e¯«Êù¸b RÛ-oméõ"ºE÷é&lKü•d·€HÉfQ„Û§Xà x”¸û…z"Š{PüC&§¾ÐwÖhÒ¬ØÍ5¶AˆK]” 8~×ü¹´È4ê°KßüBŠèöè¥øq>…Þ]tqLGÏŸdؾø&Ues¼5ó ,Ý”#"HVý Æ=/‘·zF™$ŠoKû–æ²°‡ë9†EÿÑyù´=ôÓØøàì–waíó_þ®‡­*JME±ÔЦÒe Réíí¶]›ê}ñ(I|R P’”Tu¤ŽHe[åDÿ)k_%-¼P)»üø\ª'Bµä72 9ç¥ ˆY ©iÐgh¡޽$ß:Ý›“L£®Å¿õ¥¾â÷ˆH’¹VUäÉYä¶ »Ã¥Ä •”RF,!b³Äpád´R(­®”!´·j×#)+éîk³ýܲ}õmÜ_‡¥×gå6ü–ÙæwYêW˜û¿j¡1‹`—ïÜù=…Ó…,¦ °C( (aÐË‹ØÐ7¹#Käðk<€…DÜVá¿"¡¸©‚|·S¨9ÔÚˆ¥*ЩÏTz®¸£|Gãê,mòD//€ …9ÐEë° ÖǰT6€ #£Œú+÷Ëþ Å@NHõâz/LˆÛÚ0Á$LŽÊ„2cIU÷yg2Ëý+ÜPcâžz®•ù¨<8%ÐR!bcîÌ‚‡^\¹ 'jGÐY- Φ‚vV—•–Õ§+òèÅJ–¡|¹éTÒlC%õ ³³‹¼mËüÜ@W§]±yíí]{R”ù—|qH {÷í·ÿó™OA?`÷Ý÷N6fœ³¦eDwù$h •95ë;¶JЍ'_Üñã'|xÖ·­ñèÁ*ÉF Ø‚¤Ô4ÁεZ ÒßÍ4·ÖX¹ä:»É œ brÛ|GR… žÏ–#tœvŒð™Ó,y`Ù¬ÆwƵuÄÔëKu4Û@FBŒ4÷KëÇ·õK>kDH8P `zšÝ™ ˜ZQç\kf¤Ðb™Eù•¿½Ø •0WGJ¸¥8f„üËÖèŒ$€Ìvˆú-Ð|ŸÒ#´§Î¨çK«˜x~¼ÃÚÍè4í‚0.UHÄQMµPG"½Gˆ,«º³eI-²Ès¿8¦ú,ƒŸŒRGô@}DZòËWd×»d¢2¸èïÄ@*ÏkPˆ¨ûÔW4Á裇¹û ¢Ð ‚ŽBèÖæÉåXçB­ëGJ‚ûˆ¨?" {£Ûõç6ˆjº Àúïñ EÔ/Vѧ×k‰:®FEGÑiöÚyIDKEN —ˆÝçß9N…¢ZOyE=T’ÄEàÅ:¨Uú>fö ¤“U••¥Ù8Pú¶Œ‚*ÚXC¾Ì;©üíßþ KiÉÏ?³ç°ý)½ž{‘H½C,^fó ÅøkWam.:Íž_þòl玕î¸óÎ;,K„ak[;;L#2p Æ‘1¬eY‹ga˾î9Û²y›-q=Eºï$é‡,ud–ú9"µ:ëØqÖþ÷Íû¾.&è_ˆ`oVÄÙU|©¦“ïè8ù\  ±#½.$ˆ/ÄN~Ç7ø€&’4×ñkº=9Ÿ|‹sŸ³ÀUˆh™cáËÀFZô8¬Þ-³cصn¸ sk!q)€0ßIø)H&YH'ô@d•NGtxG‹À½ ˆ-&ðõ´d›¢Êƒ´ËTIÔ–zãœRb³?¯îy"OÄhò‘Mï.•#µ’ŸkÀ'ˆƒþ¤ÃKQq•ä (:© é î”ÑMBˆ,ñRŒ/ ’R× r)Vm›‘‚(šKöê°¹5Ì'éÄâLJ"ÅG åéÓ_P‰` §z‡‡(CÄÔ­_ÐKÙu¼ž½—l(´ƒ±dØ@FÍÊV4“›‹ïóüA„ +²Ï+«Žˆ½{®PNÍÏ€Ø\Z(¤k}Ú΀Ä8¾˜r± y–wüì$Ê5DîPœÞ-ÙN‘0z*ƒ˜ÖheÙ@_/ú7¹¦Áî´µ¬½}õÕW­½u+Í$A!¢Äíí؈áy[Y]²Çý¶‹ó·Þúvû­ßü \ˆµ±ªò¬9ˆ–·4!0ß'í†mö¯ß²W±Þ ŸGÑ«ÊxÒÒ\i'°%TC ÷á8Ë]×]µÉžeŒ¶ú—[÷“N¾]ßä à븣³ ˆ®C!~tšÉŽûäÑ}SÎî¿õÁs5lo(1ÁàN/ÑÝü”«œõ&Á Ï’2Ùy[{ë%D»”ZůËè‡1Ê‘QÆ)\b‹†5 rXÉ%¹†Û,ȶRo)¨ˆÙ•:9ÝÃ÷v:­I78¾€QLÁ%_fŒ¾õˆG²EhŽä„8˜â riæ"•ú!è=™¢ˆ+Ðwabþ8[õ>úîMÜ.÷d2Õq—‰dò  ¥7¾x!_KgõBîŒêjÖ±ÄO‘º²$Xk9$!°v†qC#®}ô2èâ*#péU«–{Z²÷l`…šŠÄe¹AdýTpê·¶®eÕÚ³ÖGnUë´§úˆýÿñ߉_ûÏ¿b7Ý|3)©ê‘.$>E˜ñ– þ{%‡Þ'ØexïèK`»ß²õ×ÄjAIK„³§íªË£~DyóýöŸø!—§¼^Ξ z|Œ±dñzÜb‚üÉ·(ZAÔèŵt¢ WŒ‹à4)^ëÈ¿X‡µûZ~O :³ÂK1Ûtd8µ-÷[‰xY°£‡ˆp q@F?¡'Çï¡ÆãŸRÓôyˆs-X潚|NÁes¼v†ø}^é$»d²Ø£Ë*.ÕNž _÷Oùð•}‚Øa¿¿¸ªlr»I§ŸX;ЭëQyðx$ 7I.\WöÞÐEm@ö€]S‡ô.ô©h^E>@—plïäWôìÒóúMZ·ãê‹?¤C8c(#~@aµ²ö‡²€É‰-(…›Ì=ìg(@‰7%÷Ÿ¢_¥¦hõŸ[F1—ºdyy0ä§F‚P+óÜÀHû)]g$ôfê›ÈîôÚÀ¶ÔcÅçwQjBºÝ ‡®¶ŠÇ©©~-¼„æÉ„d ªkwEãUUèÂô> š¼Ï Æ£¤ÆU'%´™0ö pi/€ôÿ40ðþQŒ‡Ï"=\,€;ç ù|Œwo ‹7žå×%€ˆýÌΓ¼Â{°ÖGú3oâ"!x8ü,z•ö@ÓB •Ñ1R“AÖ“mqý!+T‘wUÄà,â¼kn-}]%Í$^³}­ýx;ýj rÊ vp•:ñ…¿ÿ?¶yãz¢ùZíÏÿìOq§ÌÙæ6€‘20‰”TRÛØBLÚ—¿ôE»ýÎ{©÷±î^»÷¶ëüåùSYàÎ~töI Å‘öô­’'!9ŸGµÜ œ´â¹hTHê&÷Æ÷œ×ÔT`E=ë1f±o°B®ýw \’,M ¡¸©¼2¸¹Ø uºlúP”Ã9€;r FãåÏç§ÛáV‚LgHò,¦‘" …(6òý{Àþ¿òßá8³€qü”h´3* „8(„(DU ó :z´ôx!›«“\“]€å¾[qu/±nÌv‚p] R‹ŒÚ£NJ^Cïr›"qÜÑíê-2Ù†ÀZkÆh•PÑ{*$³ñX6B¨—y”§€ö‹¬ö«AP6 ZJ¡NÉÈ©ˆCEÿYÕµRBRœ¥é—¤]És¢é¾"oy¥ Ä®ÌÃ05Ý2FK‚‚ð=ŸÓüì^SØÊFb5•s^’ïøð']"ˆ(ÏðèlïÄt²'H…–kO@­qV_Å«pïM€¨:¯¢ü|ëȯ¢´a7b}WÝùÏÒüÉU(ëz@H–íî;ØI^xVþ¸I8zOé‚0}/Ãèôû´CdéùÙŸ}¿]¹ózûyûÆð÷'+ö¤‹W± y„ܯ¼ºÏnºåöÛÀÐà]µ¥ÙÖ¡šô •x4V¹£GœõS L¢%Ágˆj y“¢ß1ŸIXô6K<ÑëùýÞvr_ÒßZ\vféO5ñšçˆd›Î3¶+[çA¦”|âB ¦ÈÂ(O~Júµ8°t\f䇷l+„€ÂÉÚ¯m£/ï$½×&îó qºÐJ¢HÁú©œ¢7 …‹ï¾¨çuîu#}Q :²VÌÙuTãqYB8Æ!D×Ö YáµxHéÀ%¹R ´2Ö ‡ d‰íèçž@*EªE "t"Á9•‹ÞMcÈzÄ÷@¢ †À§­´³Õì9µÞ‹//H³GȈòˆ ‘¨ÞAx>‘šîWT¤€ßŸå®³òº @Š'Ó$ìh¬œ¶þÚ1›;=Ááñ?K^íCï–"zº+Î1WÏD4Æ·Ù8ó EUÀÁâ —Mˆâ2烲ü ¶p°;¾3>–v×ŵHGnA…êR¤~á=ÈGÜÑ?D×–¤¢Ì¿2úšþ|'÷H Fœ—>ŸÞÁÍ‚1Æ_@U;}†¹àÅО}.öKô—qS×ùè›ëÜ€2›„VWvq<êÉFq‘Ø«Y¸úvhÌk˜ë°).Úô>Õݵ¥1”¤"ÉBnFOacL] p‚… …@©šnìc¬n*ðÒhóÙÐpEx®Elš“"pº¤Àøè%Q€Z:<ï;èäŠw<\R÷<.–$Íûtf'ÇðßO O¤jèKØÒ”æuØ0ƒ~)V^Ë•óLꊻ:Ý-²XhM«!óyBJY- ä}a‘†¼+ÖÕØ+õúú4IL¸â’вÖ655ÛÍo½É¾úÏÿŒË°Ûžzì;6O(çGéÃö'ò 6¢xÞº{ºXž¬‰a¾Píd£2ˆa²wÕ†µë°¼Nذ–6#Ód÷šK?D¨ÔŽ&ÎëQŸެ 5uŽë*þÿ^¸®±TCMêDŸÉ=É· ªIER@râŒï¤¦n‘q}º €ÏŒ°3PÔp"÷·ë>·xKÌ„ËJôõ@‰ÝœC„ k®_¤R©_‹Em§O3ÇkH=ž; w}úH¾×_)°úñšÂN—¼¿øÍÜ)f_F¿œSâ³RÏ—¸OIy¦Ý˜:ù}"ºGc‡ˆî‹lô•[©(ÉR ,<`´@â>DÁÕ×ÍuDc£8¹±N¶6Jö>®ÿ>] ¾ÀÑ ê(‘’©evàXÎrÏód7ŽgÅë#ž \Mû ®¢ •@ÄTÏ„¥q“P^¢K!FÈ@q.Ƙ(aŸª ‘‚Ð"!@ŽÂ[m)^ñÖ¾ Iòââ¿”H‘—%@otá%zñ%Ý‹¯ýÄ/ÚEÁ¼ßÌŠÄ–¡T&µr2Ævº”ª«Ê…3Å4SÊZ|<ÚÜPÑ}¼¹'çÐÈððQ;5ù6ÛÆF ÃdçÑfŽÒÁ•Åçk_ûºÝqÇm¾ßÜ¡#]68–&¨BÁ «Ð ¢Ê8{ö¹—Áƒ1pÿ¥§ì¦«Ûì·ÿøKÖ¾~µ½øòköžî·{î½C7BÑÒâ˰#¨L±GT†…×]µÁ¾úMæ“aÈ„_>ç‡^¶…„€ áË‹ÎQü ç‰ „æŒ: âë´_ÄDàüH¾¤ „ÛJ’¬ÐÇ©atné•ô/D\æ:a'Hé5 Äj|Ô|3?"Þ„ç/a©O ¡üã/uF/Œ@\Ž‘WôÄmË#"±**ÐQŸ%{I§5FÑ8)'€8¼¬ü«¥Ë»ÛN.@$Q2Ù+ÒkArŒÃ®Â€r÷©C"ÒûELPeB;i pIj”ÙGAF5W Û9Ží€@¸–&^áo×½<ë¯ 0³¨6Ä:ýÌQ®Ÿ99꫸º¸>ïQ’J%é‚s28zšp¦Šx.Û¥RÈáÞLuÓÞ~¾‰7P`„ýÇx*"À|£¾‘ŸBÌHä:L¢¹[3qÑ咽уå®D .ôUdr—£°®)°‰™½S¯@@³Ì_±Gâróiž|äC Þ…–lÍrR}÷ÂHCE5J ÑØØˆÛ.#? ¶?=›µâ÷§ˆTI#H²P<ííJ”RyùåCl1µ’ñRPÑ<áÅÙëûöÚ»x]ݯ»+Qhï¾}^_ 8Šª°vC»»Øí¿~ò‡¤€hrx_¹>…ØÜæçøöQâ\r\ޢιº ˆ°A¼¾*éZ yê_¹PÞÆÙ~s§IÍŽûišÔV5جØcG”ΰ·æºhåˆï»ùpOd‰G„•ÈÌ2×”»ÊÊ07éß)ð®ô8¥‚¿ƒïE¿]½…NþˆÇìš P:\`êûYz)û¡¿žHz<6†9TÆÙ%„ •ˆÔâ°.pŽ@ñÝŠOûbŸ" V*ÂÝe«± î>Ìáéh¸"ˆ/|dµ\ öÈܧy¡÷ñ˜ëxoDãÙÿØf›!ˆWpê%N*è=ø@¿Oñ,åü×˪¾2{´L:DÍíÁ÷Ôâ#ˆªÖ.¨T•YàWAZBhŸt‹y–´¬c€QÀ Òð’Á‚Ÿ$ *©Æ”°¤Ò…,‰ »×a’.ÃÙдØ£ç'r‹Onm^b j~>­ÐE¸~5QOc$ =mo%G]׉ÓV›žµkv¬fg–êÅ)2Üig жcŸ5ÖTÚå·^g÷ùÿëu|ÿˆ‰Š ‹BßY•ãWd´q‡Õb{aRE;ï'àøÁ}öã'Ÿ´~ø#¶sçNl¨ §ì™ÛÍ·¼ÍëâÁ sÂ#¢_,ûÐ3eèôIˆ*–]Õ¼ªJ2{…òötŸ‡Èù„FmQImƃì·,qž£¢jåŸzzžŒœÅáz2n(Ö¡µ×1Ö"þ¦£Å6€‚,ýeF>G:7t_4RA™å\N§ZÜ›Y¹¹ýVÆ.×ÁuEíñw7§@žð NÑÄbØ1än“ÇA[pãz ¾$]í´G•º*rP’€(‹ˆD!Ðu©Êô+`ÁP° ñ !âyÀb'/²Ð#޳ÏÓ”ò´{›ÞGUðá…n`“_í±ÁOeêQôj a"\5³ø L(èñJ@eþd<…£+Ç‚GQbÜ &^¦ŸÔ‘”8Iz6D~±*Ò¬Ä”Š ‡Ï9K!À‚…Ðî¤Òë%í‰èKjS<9+ûdË‹ŸŽ_ð3tiØuƒÿÜì䩚:2¨çIpZ ¼ËšIGY\£Ä¼ã„!°¦‰Xœ1ÐÓ†¢²âîa-@K;yý‰ÛŸU]jc]—0Šh¿fÕ*?7…`yëfRYAmUhŽä£*“„ ‹h4Ĺz»»íº­õlà±~Áø¨zõõ"@ûî÷uJûþŸù»íö;|i°òü·®nV5ôùöe‰‘ÛÏ.ýpî-@]/üVvWfЩ»&ÕË™„¡ì¼,¾ª·Mrt—#¿%õãóçó¥6†ˆ–™ƒªåøÃûA*]@¼Vš/Þ×ýiØ•Ž[È(&±é)„sF+¥g—•&Åû¬€ä[ÍfZÒ½ Ï$ü8Ðbn~‚lGpF¡w’.€•0!ª»ïxn” éAIò² ¾ËH@M8»×ƒ€BöfúV±ÁÂú·A8¶ðád êžvêñ0Ý)rê~Er(Í_Gržù-c›£°Í¯CV]kÖnœç?:{1µÁJÜs€qsI–/Ä~‰û¡2aìs]j‡V"J !ÀÊPtžÑüå+ `AÅJÆ»‘%ÃOÓÌŽ”"û3HÚ’‚’w¹¶îHÛ‹Zlô‘’ÄÍ}š,/z…‹.—Dœ»9y"Úlzr@½a¤DÒCN/½*ˆŠqJëÏ+$Bap!«øÆÙ…÷ÊMͤÿÞζÐÑûnPå;ônÚ¸ÑïQB‘¹Ù¢ÕÇ {²ì¥$¤ÞÍÊÚ¯%Ä¢®‡tàMºÌÚiS™‰’½²PÜ|¼@IÁž={ìK_ú’ýò/” D»H\zµ½÷Á÷Ø×¾ù°K ‰øúSIÞâe)JŠÞsIH0Ç „ ©”|û MsÑòéä’¾5 úNfXõ’ßo"¨UÝ©e&SlÊ‘E²Òž|a-ˆškE`åæ ¢à’²È;ëÑ’UÕ­nå1âÆz?MjYQGtj }D¢e÷q7ÆÃ¦é3\› œÉ“ÌÛ'¸}D\’ë ç·ŠëÑ ·8¿9‡h-_‰l¾á Ö{Üú³ˆ)®Ê:RÕ5¥üÅ(Á‰ôi‡2½ƒÛ@~Ù ./Û€ÞW#BŸ”2y‡øŠK±*0m \öùG³6T¾¦càWøàYÊBddúç÷Õ…²ú‹h2'@ u”k‘¨E" ‹Œ‡6”-å÷³s1êp•\Ó Ix+›—~´[’¸½§^S’*3MqL´yΉB(OFÔQžštëb¾/‰D:LÔ¶(&ŒÌ9«#£žªçB…Ö*ë‰ôóVw„,`ž=ö®$×ód¬±U,ö¹üòŽðz‘YDz­ñ?EÔ `Gåøñc¶n9[H77úqŽEAc%Ej@RCÛú6êµîSsvçí·Ù“O=Í:fëeMÀ2v!ò%n:Í.Ar߉|ç‘ïÚîgž²¶ÕÿÍÞÿó°n|‹uvõØ¿£ ÙWñ "ZI‰Èj‡lœ|—#{D “Þ'wEõühá¦xŽ…Iëeõu“ÄAE¸·ØoíŒ8½Jc]Î"rÈ ¬õ:)#{’›±/ é´,8‹Q-»žéÚÎÃDÕÁLï‡kb«È9·²¿áîa†_%¾â¢íÙs_0ÏfÓèá7p'ï3O¿“÷Kš ëùô4”T"Ω`Ò‚7¸Øf[©£Ñå­!FZO-òéP[ÜH©x|IZ}óµ+ƒŸ0y'…ö ©´4˜¨Â@i¼ <Cmö©,Ò+iî² H%Ë;Ç?9M<åÅsN¾vrQ×@#K³PC&é1Vc{b¿CbäªëÆŒE¬ÖØRk«Ök§¥e6KnŒ ö/A9óØ´4Ûn#'†¸T3Ü÷+0ÈÞ‡¹$AB·ûKòSzÿä ì®ó$„i˜ Ì'Ü9l¼q^¢’••ÙGv´©F0ÛÍcn+Ö(— Ä{\•É!ñÕìä'ÏUì=êƒ,õžÿO9yß—#­'ý)¡zÊŽ—¶“<¥Ë=-[ŽUÈQòÖÐ_ˆ£¤^œ^nÇ"ÄE¶­Ô|‡e ]V›ë²úöi«obïÊÆåVÃîX5õ•H¢‚åX¹Ú­Ñ2ö)Ô܉Ñqž¬×dÀRB’ëc4¯¡fÌ Ë6=†Sì895×M†^oxâQÕÙó/ÿÀDú¹>taMqº@Ïaúèùµ¼ A=H-2pLAeßúÖ›m¨§Îÿ’}áóŸ·‡|Œ¾öü {íÎ;ÃNåÞûYb´ö_\7/"'€h¾˜boúV¯T×ÙÐX—ÿÖ6dÿèGvùŽílZˆgžÙm®ÛŽÎ%C`„èìbÄ€BÖ)ìm§“®ÏÚêYvgÛ±ê9BŽWÚkýöGô‡¬FÜb¿ÿû¿oï}ï{íþáþÅm ‰þúÓòå%ÈOÛš _³Y†üz¦Êµøˆ¡‰fÑN¿}&px?Q>»NqãKoö%à‘v€¥J :‘_k¥†V Q­Bâ2€ë»Õ"¦ú6ZZq'‘lP‚ Ÿ{6‰P„”Ä }ñ“ õÖF@3CÔo‚˺ÌÊ5“¸6a”¥ÖrxŒs4½œîåÉlõ·ƒD èïÄðk7Î q£þ˜ô9‹[øód 0©òT¨ƪ$?<ÉN´í¶RÍ·“AäVª„!%ß¿ˆ@<ÐÂ/•àba>e)åx|j¶ÓÒS/Y RV}In×±we–þL¿’È>éú„¹#~¥*ÄPG•üF´À^p^ßÔDÀÖ4êé ÆrÓ¸¸?)íIç „h­¶l%áÁPã•3ôålAg.¬0]-{ÿ.äLJé˜:Õ(‰?“®R¼2p€Ê¢3w D<=hwÜ~—í¼úZû‡/|uc4R‚ó±ßüÕÿl¯îïT³¾^`å*ö~§ íý'Câé¡QË÷ÙæVH'eÝV^„¨°!+O!ÖWU#úQ^?ÜaïCÉÅDçšXƒéwBõ vžbƒ’B@îßo×Üp9ŽØ×ÿåëì`ÜißþöÃö|ÀþðÿÐ~áçÞþæï>m/?ý=RçqwnH‹`JÏHŠFpæE}F‰ÐAØat¡2dýÌÁkô) ×vd;³bÙ±8à‚]  M1…›j¢/²¨+F]ÙvAfç¶BpçqÝðÿk»îЉÜ`èS‹uw{ëOQo¼çn¾JÑ&ÜÍ­½ˆÙ˜§ïætyIH”|ão£E ÅÏû¢ÄwK¦¥’ÖÛk™-À(µ6º>ûð‘" ‰…‡Ë¢D–h ƒ" YÔ™ì ùL$k½ÞSj‚܃bõ2ÎéN†Z4¬âYÞå>.mÀ ‡./»õ°¡F`×˽&KISÀ8GŸUåZ}æ2MZ#Yk«ë"ovNα誈  ¡T%v/öøãå Ygâúêy¢e3 ¢’èÕ5U=›fS’¡¡NbC•T'çô‚J$k0”S }ÂèÙÅ—K!ñS# Gàv€‚} (eS¥ŠyÂúˆÿ ˆ¾Ånµ“Õ€Ý'ì“/>jïÿÏØÇ>ö1CÑžzê)ûѳf×ßò€ ?m'Oƒ0 ø÷¾÷={ì±Çì®»îÂXøËöwŸþ¬õvì·ï?òûÌg>K&´gã‹m$¼ÓâEš?”¢?7€8z¡Zy]ͼêéÅvÞù.!Ÿ¦§„þ@ªî·¤0‰ÄEéÿâ e9vq[ÇŒ‡‚Ê'/ÉÀÃ]Rë½P¨Æ—^K±5r“Ջ˹ ,•Oí­±Î™ þ²Áz¸+Eˆ¤°ðBj„ød#PñRš7Do‰ëþÞÖR)}ŸìÑ{HIJâKÆ]„L®Lõ5‹„Âöb4Œ~Þ-ÂP'õ@ FÐ̓éî"TÒŽ…ƒ0¸[¾“¦´!ÖLô£΋ “D»¶J_9<5ÔíµÚÖn ÒÆNîd<$Ú#µ°³È|!ÏÊW̯rM‚Ø„íbº›#ÕF/Æ:`:㱜!eÛÈà° LÏ2B=$:囇êÑL¤Cˆo˜Jã!ëShLúoNeÀáÏëã߀øs<ƒ%óT*Èl``P¤ÉÐ'ý×gpy€àJÐ1„ý¹Ï}ÎvíÚe·Ýv›ïPSWcÓã´R9´–@–{ ?úÑÿȪ¿Ï4T´·\½ÍÏ—H53#í,*2Æ9à^•‘Á“ˆSE«CÙrO$çdÜ)²6!)’0š››­¿¿ßíÇ::¬}MÖ”MX®H!¤D|¹j¾õ­o|“vß=·ØFrþúÇÿÌ~ößÿ¢ýí§þ·=ñâÖ;`À‘J!R ½0¥¬ðx‡ƒ¼d]s»¨Î•Ýp–Ÿ1¹8Ë•äT\Ã)@ c*ë*CmZ_®8‹® ŽÀƒ4ÞÈ‹ iÀ“…ÊÚ ²,A‡7æE„Ám̰7`e'{ )ƒ}ïÁšyÛa½Ä`Øát_ ÞãÞúÒqe鄘ùN*1ß"L²?hÙ¯ú”à’ 4À—ï GµÐGª‹ˆ’ÆW‹nÄÝA8·H*@Í‘taÕW€ÝÝt–9Qÿ!x%œ¸7B‚š÷ ®QÙΤ ¬ÇüŸXm•Ošµ#Ò U¤œOe‰VÍ‘MŠˆYŒt%$ÜÊœ–úžNFåÇS|sæ<‰á±‰p9mcäÄ<=j£Ã㨠QV+žh!›ÃÏd 4¤‡_ ÌFËïù™¼Bü&þu© $Ð %rkxŸ$S|ÏØ¨äaSdR%@×Ò±x“|쯼òŠ}úÓŸö(¿}ìWà°w;Òé%…‰Å]{ÖËE·iËf;‚¯>][e5YtL”"-ŽJä®Ó=Jú©2prÜwœÄÈPi)õX¥ä主ç9Z=³°r öõ`µ­ÎØêuk`ùM|$}ù/¿õ›ö݇¿IÑuöÿýÉïÀ9û]ÎÕU³ §â·wÆïPþ¬¤-ó¿äß s©t]×ô;)åjãø“J$;¨V` 䛯f_À\M†ñ‚Ó"FgÖƒRû‡“*nÝÝmØ~‹áÍ-íxlj/HJX­¢Þ0Ü%ƒ)€®üyzú8¨ø]¼ß,ÎI±B¨:"¤xI2÷Uà'uçzˆá~€aaïàÞó€ŸåVª¿ŒCE·–U˜‘ßÝØ~K.Ì’¢ç»é HÈ­HB „:_ri@œÞƒ«ç3Œ|³ï0ù**Æ WÙ¨4ºêŽÞhð‡fm<.XK»ïàq_¶ñWÛìËB»l ‹©šŠˆè,äIO{ˆøpôJÂ[kk£ùªªáyH/)†qP¿´hG’BÔgðP ³âôjéK/í·ÝϽn¯í9N†¬©A!–2D /4ÿò,™e·ÈÁŒ@EØviEÄï¢K" ‚°’¬Ÿ`·½¼Cp±_ÜPCí‰7Ø«^[É3P‚«Jü–þ«_ýªmݺ—@îG*¸Ú mÇwJX W•^ß?0`[±èŸè`Ïú1ûÈ/¼Ó¶l¸Ì³û&/ >H hjlrC IJ±ÌÛVlÓ± fk ùÅî@‘T ­Ê´|ø!Œ|³)pÏ~rä  Õà—Œ‘˳¶i}“}ä£f½p¾ßþþÒþþŸþÉî¹ë^ÄD‘w&?AZúr¾Å—}útkò¹K€ÃWB(¼FLÞtCÐä¡<_mdÆ€ðJ6±˜š_Å: XÚ @ÜVÈ£Å,2ŠáêÓV[`€ÓIÅY‚™×¨CÜ{v£•šßÈIw¦“‚œ üó”#0׫Á1ÐzRNëáZÒ«ý][Õ¹òlxÄ\ýM Þ"DuyJj?Kh²#¶ ãœÜ÷Ú›íDŒ§?px2 €µ`"Æs :Z٨徦mÀq)z"Ñ jAmuPcØp$̶1ÒØ¥dèä%šø\ÑR²÷}0e¿¸ž¶ìÃVYu§éZf';XÌØØ)‚Z­¾­ï@Åw+ñǯeÃUÒù‰Ga™.ö*¤A‰ßéé òU ³êt’,Xc +Ñ^ Qˆ^Ò‹dú4¨-~'|GD€ñ\ù”R¯àÖSúz@Fí³•K"‹0õn6?uR&6YÖ3•UˆÎÅU\õU:‘ÖXcœ)Íx¼‘€¿#Á!¶’}èö÷ÿÌûÙ6ü€¯ñPþ}mÀ!Ѫ¡©Í?ŽÚÝï~Ðë;~€ H”—è$]Ž¢æfÖ à6î·m[¶)¨çÓól%6"=˜Ç¸ØÝÝé[½ûÝï¶¿þëOúùá‘Ó¶n%ù ™k† —a¨hmìQ0ƒ¨öâKÏy=ÿž}ý60ò]Ûuíµ~Ž·ô{’ï¨bÙ'÷$Ž\…nûéY1ïwä×µè?Ê$ ÿsJ\ƒó¨„|Kvžá›plϵ'_¸¦Lâ6RÌß´·ÄM=1ˆ RKtá¦aó­?Qoî–S«Ü«[@üÂIžu“N-㣑þíÁøBІѶΪq¨ 5¢W¥N\¤³Ã­mŒð\Å$éñø¿âèòÛçhO Qu¾ªN¼Ì•öYø½O¼«¯àÙ:§¨»Ú« kx£hËnrL#ᯞ%¾Í +m}KƒÝvÑÊw¶c›`‘— n³‘ÓËlÿžy«ÏØ ij¶n‚,ÉCvz2÷‡X16E¶ú–ŽžF ¨À?_˜W|ά۩FÇÝ!öÁPÀªó,ØTB6΂I1œÓ9•ˆIòí–Ò3J5"(ÐŒiYqKoUU¾ rIàÌ'Í£›È¬u%®6Á nŒÊ4º$…èBŽZ_÷¯âõ&dRœè=a•„|÷‰'lÇ;ÜFÐn. ÀÛwÇ]rôx—ýâßc{ôØi\wY–çÑ©4t²(aU•8øi똱MÛ±*'AÄ!Œ¹‹ ˜@£) 0Cl<2Ç‚¡¹929Üe­×]^D«üÃ;ÎCè¹IÃ\~ÈÆ'ñ&¡‚`;R4ª^¯Šöûâ³xœ™Ö0v p¡#íÒ‘ÎyÆP]KUxÔ`¡Ø×\¨_ð׿ðŽ`T"Ø_EÖ ^4-å€NIìÁÂï zŽÜt3HT (àÜ Ì¿·ÝðvûûÏ~Þ>óìgìÞwÜë¢ùkõ9‚þEH+»ÜT ]|ü·ÿmFï[ŒW’‡e’”_ àùÌg>ƒqoÀ¶lÚ@º°56ºï T¸Ú·×cì¬ò©¿ù¶4ŸÆ(.ÆJÂWÛÕo‰R/&[‹½õÚM+2-%%×9•i¤×ÎàÒ¾gStzaJœqNØ vò¤xãç7 ¢@IDATšÑWŒ+KÎÏAb{À £_žTTÓsµHQÏApP1м‹RUÕ\‹»†jøñeý÷pV82qúÚÂÊõk—ÜâÁÖÃcX•9 bŒ^+6CÛ瘻–!X#†Ç‰©ëky›j»‹Këž1@ùþô#pöÕ¡ÕN„ܯ¯çæÚ°t#ÖÓ–"‰;Òm´ öV ²h-:„Ý¢àQ„$›A4ÍV¯°:ÅÖÔelyÓr«cÈ&\˺œ5Õ†vŸ,ô¤C*$ø¦’wo´fÇ hãxJ‚q›Æ{…ñ/Ç=Z¯OpºOŽà³?f£S'XHvÚE{eó|j³ß•Z½¢ÅiHŸ”hf4Œ^’±p¢°p\]Z‡&0Ï•JJ£Ì4€2 YzÃùý›¼~¬íä%ÂLîJ® Òî³ÕM5DX·ËŽWLb€#oTR6J¤ŒÎŽÙÍo{«ìç»üŠcjÝ@ÒŽJ¡½·Ü|³íºæ;zä «õP}Y¤ªXN<2‚qŠ¢çÊ1F,BWg'FëýüÞCP>'äN»·ɲ~•uwŽî-Ÿ+e†ÙÏ1+^GãsæuÝŽxÑ5ÚKJùctnñJRãM¾©ìÍ1>³EÆ¿š%ª%¬ê9¢íjoÙÖÁiñtwïëdŒ“h.5@b7\TkñKZ{Ï“}ñÎ Ò_˜2˜J°Ééq¶‡á±Ø.(iAÞÁ%EÖþéƒ\GTo¸{åÎC%ñ‹à}í1JÊXÿv®²ëù+a¿P¼B¦Hò?¬jʲ¨‹zÕËAzÚ,VW $ ]Žñw•­jÈZm ÊVcÔ«Ã8JŸj!â³ÃÏšuœ*²»tÁúqÃÍO §#ÙÍV ²BÜFOwÙ‰Áã65vÌF&‰LTªÂÛS¨¯RM•¾NÜYHŸDižé5ÿ±°d,Þì@“èñ>AÀ" EÊXAq`ò~x—Jbøô·Â=1~ ñE-¨–# ›ÍgÃ# Å!}:,R￉|ûzûÿú§ð§Ðv^{ ~õ.8óm¬Ye[r«üUXNávqò÷½ï}¶ƒ”`ò×Ë7ŽTP¦''œT7‰ˆ°m2i¿ ¶¡m½½~à°èébÇà5öô„¬®Xf§G"Î!k4ŒúÝÀnÃ*§ú»mõŠj[±n½u²œOüÀõMöðk …-üÛ£>x “H<“åí&W5y”ÅÚ:XrâHMà4ßþCm$DÅï^ü´ ò 2†¸œŠH_µ,@ KèØ-¿æVt­öK)é§ômõNâ¸bïm…èžàâXRl=H ]{ßP¸L㲿ÁùáÖó¨g þeà—]ÔéàÇD#QÖ >I¦+{¾}‰ûò4x‚Po”ÝWÒËig b‚Rj¡L ™‚« ¯Ì¬¶©=}úy3ˆX½©BB˺[6È®‚(4Úª•YÔÂÀšV¤­¹9˜ÎÃ8‡®ˆ^fž•§v9®ž°ÎÑë9¹˜Ük'ûÙñ^þüŠt6^$¡Bp°,ˆô ¢}Ù«.@ù(h’t¬ïs–¤Ê+;°-ðq°óƒs¶_¸$À1rx,æ'FØQ‡àìT5QÁt¼&_\ûöi§áG hi`WÞ —ïò.h¥à½o¿Ë^­}ÝÿQ²×ž²†8£¯ô¥¤ì'B¯£}{»}ðƒ$¾ÿ1{âñÇó‹û'E^q÷Üb¦28îØ¾Ã«lZW…G¡×G@4! ÖB%‰ª=ýöCãe„‹( 2­¹¬ÉA¥h¹pRôÌhØdPbÄwdO~ë†ø·ˆDRZrXˆÏ«žêðç§©ìõj_v yÒÄ¿ýµø`hB¦††d©npå)í78Àyá–øá=3 ®5%¬à€ñRì=Ò™VÒiÍ€tk5tF‘æ–jázš¶•ÜK{:À¨WJµÙèþzË~ŸçŸÒ»œq3'<ÔWÙå¾c)¯e×"nCH$u Ž„S{°ú#úȽ©¯m‰° VàÒ$âup®”³F q™t ¡&‰ëØc/S-jfm¦Ñ&qÓÖ±þc…¢xˆÈk€Îø£t»1-‰‡^N“C¢ÛŽ} ÓAö‹Ü‡Þ?ü`GÉTôYÂ{ÉàqÖ?½“#»3*zOýLÞ7ù_^¼Wôó|xýä&5”Îé´Ähú £aR°¤G²:Ií ý¾$Pö°¤çÓ ÃP:ZÁ7 «ÎC" ‚ ÷y’V1Ö×Ýk­øñ3Öæƒœï¸æÛÞºÕ À®·]o'NEÂéÂ#$ê Ѿò•¯8!¸ãŽ;ìš]»üX‹€@!„Öb Õþ¤Ò•x r6L^¶b­\Y§Ý‹lâfÉ9µ*R¦ÙaX†=édî=5l«šáˆ*<·ayÞ‚Ë•Oú©$bQÄ#DU[BZ?ÇíG'ùL®ë„8¾ ë*@òºqõL§’ÓTCéAòSÏ í IÙ¯ÍN#ê)ÂSgO:|º Kº»Ù”Gbà«Üf»"ÛÜdâÐæÂy¢h“”¤c@Oª¬S$¶¶çvqÀã0æ­y  ±µø"õD;KŸoa…ÖN\ËÃó¨k¸h'^d³cliÖoUµ, «Ó êd¶Ÿ9ÛçægYW±^NhwšQOæ yêj˜c"ôêRÖЂ2³ËªXŽ “X³Œ9'Â&à†åP÷„=¶ßúOjB~D"I( „ ºLejû…øDB(’ëçñí4D B,ye «˜éžiŒTXëC ‚Ç-)/ž¤ñ3 m¾=±ó.†#È)5W¨:p–‡òÊaŽ÷ ¹(ÎW„;Rß0±¢UPa‚ÈÎ3@é³”q…‟b» ”ª’õþÕ©Y«©:M“]-øÊ'¯ñêÈ}†x>=‹‰±–èùªz,ì¸ä*kp ³Þ‰,@²¬­bþ³ÌoHž ø×®P¼}%Ò]¥)ìüé#vàÄ^ë8²×ößmC§NÚ(¶‰úšœÕ³³T€aQþ}‰ûEâÔõH—÷Ù<Ë ü„SšŸ|>ü·¾¹§ü܉?[[TÆ=(™V:®u\b"¾üP)Oq¶ÞôÜ%o[¯¢·¸¨mPä *ÓXHd$‘•uÌÓÕ„¢ž$î¹}ÓöŽ[ïµ§ö— õ5Áyž£ýÅ––þÒãüÙŒw „Ì2²VÚlvš®6õ‡¬?ÈâÞ7éýžÍVžÙˆŸ÷,·Ää{„žÞ—ÿþzæE+níæÀŸ;Öƒ+.#Aè’­sÑdt+nIïøK£bT!ò×·Q“fWëɘ›bÑLš~+UVCž––¦±Í!âòH’g »­Ä¢€Îº986KÎÉ}¸¢‚-`ÁYEÒнÞ}ÐÆNíµþžý¶§ë°ÍNö[«CMêi‹­†ßeדp*_¢Æ9XÄ«D/ì?.ìÃ'[o—h’àd É…¤âßÑe¯Œ«Ý=4ÂÙäþ3ê_Àá¿ðÇѧHóùü©ªjSC-æBºšóçñ¹WÆ;99k'{:í®îvcË›yÎLŽIE Y½…‘.ÐúKŠô] _ r›sB ý‰‹>|Ø>ùÉO²}ø}ö¡}Èzzzœ(ˆC·µµ!-¼èû¼@JðÿŸ¸÷Ы*óÿŸé½Ï$“2I&½B ¤Y@, pwÕµí.º®ûSW]WW×uu±¬‚u R¤C¨ )¤—é½÷òÎü?ßçÞ;óÎdR†ýŸdÞÛÎ=÷´ç9Ïyê7¾Ó^~á[^nȶ2•ßÐÐà??þ{ý{óë—{^ý”Í™aÝh-*‰¢a1 †€1P]\‰D¨bvLN䙘´úG÷4àÏ}\!ÝÕÆ‰/וÞÕàö3‰“`f$CnË®•Í÷ö ý©øß‡fœ»ÙfK0"›v}IÐþ.Þ]X¨Su'HQ¥Â#ñ-·_+TqÅê÷æ´ZÚ<47¦Ûð£0g颔Cê˜íø&h±ô¼>€¶N½dðê:©Ï&Ö‰X²`Äø‚lx ¬‚Ã(å"Ö+Î#ðÌ`’µw¥À—¤P9i¥pûSÐv¤ÍÍ],/Ù¶ÆWl7ÌåÆîCè{tZ*"Ã1ó!‚i ððp!@˜Àê{qN=©QcÃ[Çrˆ^‰¦@8žþª—_ˆ2MüŽ("1ƽÿeŽå׊ÀœcF)úPpu¢¿Ó†Ê©T_w{C~Él úe€ $¬“`ÌÈÏùÖh9¹0×ê­z'û;ôöiÅšUpù‰ÃF´Þy—n´Ùsg[Ó.˜?ìó†ø§Õ©‚·3ZEî+i&É€" KX«¸øº/Sß.jÛð؃´M/ìß ÞÊП¾1£´ÔÚØ6,Ÿ àç[+ÿ©«»¦—^NcHu&¤((H°?&Qüé\à ¹^À>¨Ç„z?qt}œ)úŽŽ–±­…9êŒ?–;½T™KB"0Ý¢ŽUSZn®{NÊ5£ió@ì…§HÚÛìåÁ~Àfå@°ò‡FE£‰ÅÖÕ˜nÉ¿±lHýLf„€e¦£¨ÅjŸŒi2Æ®a©:rô¬ñ¤u™JÛµyLÆmV.ò÷DyÉ ‚D–]¨`ÂÁ¹‹IË ÔäFâ˶u{£¬9€¤è"I´= ½“T4O3JO91Ї¹¬òÔWBÜÁx¿aÅNìà€®r4¶ô§úæ ÄœGßá>ù]„Åc:²‰* ²û/窩Îñ!é[ú³“ü™FÔÒ¥nÕMöt€sèEÆ.HÉ ŒÙÊ5ËíçwÞgO<ù¤]}ÎÎh‡ô@ÞŸÚÇž†ÍUW½É~õ›_;É/<¥hý‚!QÕÕÕv;J=]t‘…M¿ì ôl3ˆáá€dÌA‰Q¼£cTVÀ3 @ [ƒèTVWá¬4fåó‚^²%Ë å%¡v0ž~þ¨ ùðd£5$ÊTt îGWÂ÷!Ðﻉ£;a{¡>=ƤœÁ´[õÉÀ§ Â}:ÚuI0øÜ/`Ê,×`¸xÞ ã¥•·¨[=áN9Q v~[md[nÙ³9¼`%…sÇý‹±‘âl/߉)ûé,òÏpÕ±}'d¸ÔxGDuˆqK¼Æ Æ ²^a’ÐXLAä¢ò]šõÀ\(*` ˶tœÇ¤¢ßNÜÀžöƒs©9²ÏZ:+]ü+Ñp"ÀÔ´tO "`úÆØæ Ç@PôFí“Oýâý:øÜ8©D‰;”ž@zXÜİ î+OøVz­_n2,ò‡ùCÕ\àŸ“I5ô¤]¾Þ µeãìŸ:ö‰”3åït"¯j½Ö ¸¢ !þ$à'€ö0°¨™‚}{` µso^i¡åÃhêe?øÊ¶jÉR[¶æ (†$;õŒÕÖ¾£ C› íWÿûkº# ûW᨜‚5#àUË"j@ˆà¡‡rÛQ ,ðm”…FÃmDü{Q¯È–Ày L”H¶¡5˜‚úè%çŸn?óFB¹vè –;UÐ÷*'ªÇØ÷@¥€Ë`zî#ü0èÑìN[âÆ“¶yòj4_ÆJÑŽÏ;ö þDeQ“[Þod ˜Ú_$@$•\a֥͡’híÉ|V“—˜|cî²d˜#>A®ÃüÀ¨'µK»¦µUlå§) {r>‹÷/æïv°g£-¦»fÎE—£–5Ì©Íb™˜MÛ‡øë!FcfŒ6€aˆÕYd¹Dyr›Mì[·Ž“¹m"=riC?ؾË{Ý©qR ¸ù »LÝ3Ò‰éG½T@Ð |Gûyg:RUûéz¡èi‡+D }á[iú¤¨È)Ú1n •‡º¨ÖF9Ãu†S"ê`'ðЧ`.ÐvcpÖC-@ÅM˜0ë ¦³’pZ`¨Q¢‚&‹–´|žx,4gÄ5Ä‹N/®¼:±°B¿þLÏþ¬(מ{èI°w§]ºî[0g l}ÈÞzéö m·ÿô{ìƒ-ÀÑÚO ÜyçvöÙgÛ…^èúr饀$BJš úÓµ0çB2ÝìA´»¹Ææ¢L¤4wV‰}÷vÍv&+Ùä¤íF”'þÍF%&¡ÏL•¢û!šp @”=~*6ü¼'dáe¾*ðëJ^¿ˆFåj˜ÈIYsÆ€‘Ÿ4þ´âËdj@²{m'Ñ &2†7ɬ`2Ò‘€ª>èß–«¯€œ_ ÷ÿÜË’ì7›‚MúŒ¥'žŠåå«Ù‡‰s— –P„Lâ©w ÅrÓ»¬ '›¦ÀDH£™*˜ÅÊÏaÃyqJ"^DS[5ùm3íVÂH!H$µ4LÓØ°Á 6ð)„ä´ÐRÔ—jƒŽa/:àëò¤’H êÛh¼ùŽ´ ô #W}%4< Äz:ü—7 Ñ`ÃI?¤^ D RÂæ*ïä@Q<“•¨eŠ‹R°ús-À˜´Q‘ôÄ!Ñ®øÜÁ£ãøVÄg3ö-(Î@ZÞÐPß(ª˜ aš§?ßoá ý^gk¿uµÕÁ™´ÎêF{ãšD;ÿœ LVä΄ ?÷òK¬% ý|Á‹×z“RÔ¤TìîèhåÞ¸FG«±úæ©§ž²Ý;wÛ¯¿Ân¹å{úé§í¾ûî|®ÊRÒÒÀÔòsÓìá§÷²˜a Ê mÉÒR¨Š]ž/Øý?Ú‡ªíå¢i=›ê tˆL¢aŒÆSu‚FôÛ!ðŽv¦*9º§ZèmÄkø@Œá‹9 Ž›˜ª½¸ÿhÿ¹ß<Ù×Ë‹.{}1ý|ÕçÍ!¨·Ÿ§ŒAĆ3ºbìÞgÏ$^&C²zçmH ¯ÆæhRŽÌ²¶*ìë÷"“§-5ƒÉ cJÞb9¼å@ d$ °k€©‹n›¼Kܘ›âåîp'b9\z£âÛÑUiÝør©<åQ³@GAYüØðErOíŽ:”Ó“JÑhª÷ã*&Ž?L: @#ž`ÂÐùPügÓµ¡\JxŽÿ‹žûxŽ‚,Ïñ~Ê9TK½ó¹G“ ½ QÝuÔ÷G\ öN« ¾8@å|y`íî„TéHHNÊcÞŽàáÇe)Š Å1bÙ½¼§ÖÞ¸v•UoG3­¨ù/Üá‚|{lûfŨÜ´ÅvmÛAۻ풋Î7û³k0ðÀÄøÁ‡ÿÂ^Q{¨$"dÀ,¡Ü£ß"Ä g­í­öóŸÿ·ã«œI¸bÅ {ôÑG1¨š«,µ˜€âdggŠ|‰8¸ÏÞpÅivùë.³ÛuP徫ó(áþ,:õcDÞë"qJ÷úxµ^‹†,âȉŸ1$ä8‘_JUyè­Œ`Ä’€tO;£Ú£ûäDÈê.-<ü¾=à…¤1=Îù/&ˆËhê°Í*E•@Ÿ“l…ÀøüÅHþ˜-*O0ôpè;‘°ñôgXsm"N.P… CK\( WŸÊLÉB‡—èú #‚c;0B<Æz©u#=nh­rí»Á.µ$Ž4“ÄübàåÌa…vÀãl ØÇzñD:+î#½džN£‹¼‡zÒ¶g¤”>½Êe#[*¨Ù¬9ä úÝE©”ꓨ¥ÈŸ)Ì]ðYÅ+×–1¨»à#.‰DÕj2¨ýHDf6}â^9¾ÓiDþa¤Wî¬!i4iÀ>CÒC7(úI«¾©pß-ƒ]Ö† ¾h Ë—X'X¿xfïµ»›1Q’íï¼ÁN=u%-¿Æ€&ƒ\†]|ÉÅv?>ýðEpü›±'PWF@QHÑñç?ý©²v­+=†ÙñO¹Žòj{ $ ¼R2Ú¾£Þþx÷ŸíÖ[¿e?üÉo¬'  /.¥éë=˜.ÜÄOAŸ¨Ñxˆ.C€< §nüKÜÕôÓ-ýE眞Шköˆe1‚Úkz/»ádöÿXæŒX"rDùÖð@Ï´ß "'Ÿ¦ D#"œÆb«ÎÏăj,%-=y€Áuò¾ñ ”¬Ô1˜6û+ë¬('Ïšj­ÕÛ¥å‹lsíf…T#'NÁ+[‚UìÚkVŸc§¯=R3Å–Ï :[ü…¹ÌÄ·¼þ*Û±uœú*û¯ÿ‡}ù«ÿn»+¾ WÔ%…“&áßäcxZm³[>ù Féh .±O}êSöàƒÚ /¼àÚ€Ú®ˆ  ¨ŸæØ]¿½Ç¾ôµ¯âø/Ë=ÕøYðÁµ¶%AÒ„ &å„ Éø0¡‡0½lõôjð®Ì©˜ñeÄŸ—sô3- ‰0¤ÒñA‡ŸÛQVR—lÙeÓž›U„t$ßf-FÇÕi³%¤ø¥ˆíæÛŒ|<ÕæÑ¥fÅÔ©$0=üöÿìz,™HM# –ÄŽt«èI°ƒM º¢®Ñ1 S‡@ðmÌÞ*;X»Ëšz_è÷ÃgÂ:/CòÜ“•žGy"Y A°…ú)ú _‚ÖÆŸwNìWå+Ñx G‹0B€žý{*iðj³‘þÁuYä}”|Ì4þÀ¥¨Pqÿ=Eå³m×êÏs¡`™?ŒQ¡£ð á)˜«ÚÍ!¯‡Œxÿ^½N}à¤:cºõPRëÅH¨OÇ«*ä: Z€`ºaVÉx‡1¡LC¼'M¬]UVV<ÓêºÑ€LÄ. ;·Èð.jéˆã/ZÂ%23ͪ°Øñâóö¦+¯¶›þêCöÃïßæ¢¾k_w=óÔ3ö£»~b…é…vÚâÕŽ6Àü‹@4¶Åù–: ©…†*nÅîúÕ]¶hA¹]K|ÀeË–y~.•`¤€$òt ¢Þ®½êj»ï¡Mþž¤ JEáüŒ#€1°Žƒ7Ç.ÇOBb4’>ma]ŸôSŠ\Xöà‹!3³Ï2²š¼[Ê0Ù¹%z*Ú˜Îb0”ž–kùèaddüù¹V•€~ël+&䧞̚t8Ö#]ýÖ‹)mJ[±>̪Aý=‰Ik¨·ÛöÕ¿bÝuÏÙžÊÍ(Qñ° €V ‚˜w”%’d®8‘è"±™F G¢^ñÓðõ(åúÄà.<Žþò٫'€°”md.Þˆ.¡^úÓ1FÐø£ÓS;döTÑ£¸cÐ K¢<Ú8r“Ø/AœQÞuKYIØêˆ‚T_£¶y¼éNhõ÷„Ø(€³¸ðé´"Èb0“<žv6dçÏ€14  F‰¼v¤žâx‚Âd>ª±_Z>Ç^Ø·…ý_“Ï.Á" ®/ªÂsç.3{f˜IE|wTCXôl~ù9»âÂËìÂug[Â_õÙ-ú ¥VwÛ!<)2w©­[´Æ~‘øk+Èú%Y¯Œ8”r–XFa0ñúáFeâIèÁ‡¶ƒ‡*ìŠ+®°ë®»Î• ˜E5h; tÏc[Afâ4ö#= ž7‘»‰?–á°“‰è@ï©ì Gúi¢9NªOÓOŽJÓQ‘¬%2íâ¥!fbvò™C¸mÃ. ›~HIÁa®o3Ñ´C9Å"Ú`Y—i’­¬é,lý(æ¤à`´¿ …-XÃ:ÐwÃãÙ¹·ÁvïE!§ò%Û³‹uu‚ Ðð(ò`M—A¹ mBb€˜N=¬îZåÕò×*E½ÊÇ# s W¯Cr£˜ä+8ÌÌÄþ+ÎõGÖ±¯‡9%ïm0™¦¾i(í¸¨1z>ù(8%â·PQ˜Eݹµ# zŒÕÞIP݈<«(i¾:/)š º©s¦…θ ßÔƒ“MÓŠ¨ŒWl¨¿÷àx£#‰RøIAŽCßRÞ€ $Bh­‡¢¢Èî.´½ˆÄÂ>7É]ƒ¶pÙ Û¿e¯GúMÃDW"¡žÚkÎÁc ºá­•ͼ× ?Ϻط_ƒQÐ#Ï=n‡ví²ùËú°-XmÎÞà\ÿtÔ{±ˆëÜZc3ç[ˆùh“VûÛn»Í.¼ðB ˆ ?%"ûÄöýákŠò«4qd" W‚ o~q0èÑÔõÜŽø£2ò1©™9an¬ZádaÚºàfÒ‰ ‡±T$xÇP¾ÍÎÇ_ƒÄS©¸ðÊÆ„6{Åp²áÌÀT(¹Ä^¢òš 78•£Ûb£‰Šw&ÄéF'Œƒö¬: ³â«<¶‡TH{Ýà/¨j­\Jz"¸u¿ÞrÞVÙ!ЋD—ÔÃå PtɽöøÃ hÛ5 Dv«ÙÓ”¡:èåiú5¸Í¯Fta Tüy‹Ìþ¼+4†äÞ”-¡¬D™aóOÈ8 {Œm9ÄôÑ,„14 é n13üÛÚç”—u£$ÞŠŠ ûñL ÃÄ"X‡Š ä\[rGƒèE‘´JN8ûX2BñcæÁ\Î9N¦2ø€>á%ÃÀ'¡×ƒ3M2&[2ŠVÉìÓ 1¦Ð3ò1¤ˆà=™M}rû÷^Ä\ùsIÉVgù^ÄkN7}‚êð‹[^¶-{^°íÏái¹Y}[¨|‚`¦XYþLQ{®®ÝÂWr•Û±½¼î¨ˆ„¦Û~ßONà'¾<•©µXˆW+ °Fô¨Ï˪ptbÈ ¤ÈûÕüáàƒ\žœùÆ™ïééÊPéÚ­‹Ì—¤XÃtUò™¿Ñìï5{ôj¨$î‰ÐW'§†1·bŸƒí'HYœ-¤0”9ÂÖIR1ð'À#@]Sa¼(1ýаC 0äŒW{<Ó‰M3f(>ÒÙ{÷3)0Ó&€‚öJ†…Å •ÂÜ…'à®zÛæ-¶`áJ…¿þšJ;eÉBkëÀîò`?pç]› 5oƒPH8Àó꽿û£­Zø6V¥ÛŽ/{6ßk¸î}ÈÜÝþnÆó±üRÉóÌE3m;ºCl3†òÅøyöŸVNý=þøãnZ|ýõ×»´@ÊC²<"ˆGj“ŠipÆ?ŠÕ¤T ¦[4´S|JilJú$JJPÊ«ý“N¥ÓÇüJ€…fän÷ ¬ÿ2ˆ ÌDDÌÕ×™²!Ùçf±ú£hÕÝ;lÕ0ðZ^²-[wYEã˸Ă´‡2È…Q˜àÏÉ)rqè c9€8!.ÂH¯G#íH(¨CÐÑQ«ãk¯{SõB|žÉçñå0†r:‹š ºÍÅt ËîèÙw,èÏb¾i¥?Š_¢¶à«p¨ u U]¯$Y…Œ˜¢¤­­ˆtüŒÚÜ9|ŽëJ† 騫F aøª=„ñ©¾Q3µúc› I,N#Ìe=ð-’¯ül¤‘©ücýB< n OòH–ñL~q"?ã­>‘·Çß¡yÔ&$MÚÆž¦ÆN:páÄ€ö€rC5B`EAQ7ˆ *2³¾¦ŠÎœkYÅsÐ$Ä·:ËS@¿{û>KÎì·Fœvvö¶;­®r½Í,LG ½6'ž2åtâÕ±rËS¶rõ¹ö•o|Ö/Á¢¯ §–(õö1!H-­Í¶á¬Sío}»ýòîߨºS×¹.A´&-˜JkЫà;ßùŽû¸ñÆÝÚðþûïw‘¢Ê‹tñ:YqÕ.u††p<ÅõøÍ`îríwTÝ‹ê£ëcö¤Ì>T‚Ú!€D§`ÈÈ‚i§IÇöTœÉžÌÖ+X‰vÐ^©ÚWÜýøÂ;ˆ¸n/²{ܤAÁe²'Î,ˆi?ÈÄíGZŽ_¥˜îP,ÏÇë<¹UÑ5ù<ÅOòøóèyü1êM•A_¢4=â6—ÕK×PÅ1(!…øÐoèü´÷ä‹@¯RR™‹šà¿ c¥g2rêæ˜t¬ÈŸ¬A†U”N´Njá"°›\9ÛXˆÌ~òüî;SЫKFzf4†ƒ˜°?R£0þd)+ßÒoAÌAÕ€ä+‹c JHÂÆ-@¯–žªðiIÓ…Âʵ‡“)lÕ ðÍ@$8:ŒÚ)k?˜ŒúüÉ0œkŒØí¶`þÂÁ Ù®ƒûlõº•6›`žý÷¥X~^\÷TVà ¤ÈŠºÑ¥†¬œƒ‹ç%³¸ÇŸEó‰Ô3jçœ~..âQ A‡¿fón«_œ‡ÀSÑ.Ì&öºð8žq[q„Ù d¡ï—Î,µ›ÿêý8 ¹Íµ±"D Æh" ÈÃðwÜá"ÇK.¹Ä-Zä[‰£mòöC±ttÉü”U1B×(92Wq)b'Ç¡?\·}̃s½啚ë±"Ÿá‡cl!ļKG÷?…}n:1}çK\[ó>ë¬8€]Æ.«k܇<ÂTQétÆ(©@TÄžƒÚÏ#¢URÌÿÓD†;:ð4„¯utõvüyÔ2Ý?×µ’ò*镚ô"qÎ# ô†WcŒxm¼´e4"njÇ‘÷Z„ôÉÿÉ¿7;綉BA•„K^Ãþßìò·ÞÇjŸ6g©3¡Ò:º­Žxj™„Kž3£ÈjÚS¡¨RÓÔ¶ × ¬Ø1·ÛöWW!h~ß®´6öTÙÈò›š›¬%?Ë>ðØ)ËO±¿ÿä?X&! ²YÍ`v)%Cßxñ5¶ë•öÈ#Œ!¾€Z^o&ý¦M›| Ç#ÒZñýl ZQc¬ ÃÔlî$6,nrY"´ûÐú4æ‚ßн±û“ŸûH‡™£wÈ£t¬#>öu×êŸ3/1\aÞ T³Õ£gßßSmbºëîÇ茕 ß%3 ¡j ³TÖƒƒŒ“®]„ % Öh“çpüð\­™*E5Ñ1jqüyô^”OeÄߤðÌfZé# ï`à€ƒa‡þ#‘Ñ'³Ò§/åuÁF˜\ûŽòâÈ{U&·šhÃ… Œ>1û5ö]h9;`ówiÒÍgçÓÜËÙçkó€üFàRï¶s=bêÀ^³†z³ÕkÍ~÷k³å‹ÍöíÀ!UWÂ(¨F&Âpÿ‘ºÈ'†á¤I÷‚EHÐÁJlTáL—ü|(˜9:@ÆaÅ›ì p¬SÉ{â)®ÇN¼ðMÕZIýH­Õä€!82(º08"§!:BuñÓ¦ ð˜áž~HƒÒÏàvö ÛÞÝlÙêå¨ÏÀçd&îŸQ"¢˜VÄ(:ØKæÎwàìFË0W3^x!rî$÷ã>£l–mÝüògSCöö¢¸fÝ©¶ûé-θà²×y Kà'pã…–‡# %qa§J"õõ'—är;&Ǥeeeö·ùˆmØ ¦¹—ê^²†6V‘@àÀàRvÔ+|¿~Ê;-ê9<ƒÈ“6ž¢)^†U9¾Út)V||¢°¬kÃòòùí¿±g¶ÜjÏoû9v¬°SÑÄ,ḂȎw±éïGaK )bâ)R³|Þ+IŸ"àð„öƒeüµÎU_ý)E/DÇ(¿®ôZI›IÝÐË¢C“«9†ÎÀÑç?±Šÿób(·ìw3­–󌩬•~lµjüaÒDghÜ1 K©]ÿN³+ÞÀ|â³rV¢¯ª‚=´™í”f?¿Ã쿨AÜK§å¹?ù$«ÿç A¨ÓË©>!}G#•…ô…l;v9ÉÏ úOþU¯¡A"5ã Wþ õ¬Wܵ±1‹€ß»Pû™ ÉÞ0èÆë§OL[ï¡i*] wÒÛÝÖ˜ž )ãyèa‘–Tgr1¸ÃC}ÈŸ¡ °”){Ö2œdÛY³Å®]–Uvp›-.'D“²®ªÚÊgÏA&OÌ6xYDuP8/M˜¥üïvÖ„K¬×”î![sÚz«ß¿Ãv½üŒµÌZjwº`¹íuo}òom"¬ÊÇ_„ïÀŠäJZù§J5 ò_âÂË.»Ìù«V­Ä)部¢)‡wZ¨NaZ‚Ô0}…hq™:øÈ'ˆ&ŠÓ‰|F_Òç4ÞNupô±çÉ€Ó*¼ás„|Ž‚Ì\iåf‚²ü bc;„!ŽVðŒ±a|´XN@è÷L¸ý i °V­ôšõÚúJï%í÷~Ný®~ôí â>ì­ð§cù&žL*$laÐzï ² K è5¿Å§ƒ&s$ŒyÂð_é-ò>³<¨Dô)VúèÑä£j<6´ô•SÚÊDõ"À-jw³Û9ïb"|ïv|pøúÀ’&³6Ù*x‰ò/ÒôAÍÞ{£Ùu×›Ý 5!ê^ºFˆÁXÐ`"¾î¤MŠ#\V2° ¨"ìA’„PJKdñ†Ql󼓣ê{•‡ú‰FâÉG zvÒGµ{º“όޞú p„cðX  ÆÐ!§s KNËd%€ŒnF«jwàMÈù±c ËÇ ô®}{m´½ÓêPÈ™µ Ü­ôZÛ°ÕGß`èc4à=Hn¬Î>ól+›=ÛñïÚYëlÿþCØ’wÙúõ¬óâ¿<üȼ[vÞ{Ë ïò6/Y²Ìbéiöú+®DCn‘ßµ?"j@áÇî¾ûnæäæÙÍ7¿>q\“‹.à¿TšÇ-ìë[@ü9ûAKI_bÜü€Ô–æ¢w÷uÔ3…VÓâ–ÈŠ¢ÉåÛGž (2z/“´ƒ)ªaNÁ‘½=4l/Ü­@*žK~óUŽ˜xÄhtR“¯h…àG€!¿1@á[ñ)¸Õ2þ‰Îè°OΣëèžÖ[ý)eð'¶É-ìj8ÒÃçAéjæKÉü&~KÞÁy¡?sãçšë‰+½—s”Ÿ¨â½Á RxSÃRVkÎlVqª¥=»š¤ƒÆtÉœB¤pûaLéNËçZÅ I4pÿ¦fo:­ã¢y¡> ‘´DFOâ™Ø\gþ9•¨Ž*Iy$®È92Ü\ò|SbÚSPÃÑ‘©»À$Þ^a>iUûäF\d¦(ü k%PÜùeŒ+Æ&9X”Õä¬ôy©û±Óáœ:ÎX8ßZPšèèm±3çÚ< Óõ>ž%l7ûø}{00 ŽÛÝ’í¹­Ûðã~@ýÀŸp‰C'äð@ß û»+ ¾‰Q¯õ£ý`Å!ïwQ("é¼· úKÔ€Dƒ·~ûÛöÒK/Á<Ój+e­ÖA[…’Ñ}Ðu³§‰ R£o´TÇW!ŠúšpúÔ N~9ð5­9>áê<ÆA3ïÔi›ÁŸÎ…”'â £vP«’_IHÆ^ÈFŠ9yŒZpÏs’wâDTæð/Ì0v­Œ‡eÖÇâî „ô§|q@ŸÈ– u ' ^Ðи ÿO€þ­ì›ç©•>¤€ ÒŽ#éËÀ¼ÉïÈi,º^J¿{ZŸ“søû—÷{XÙƒ¡‘îÛ7Í[}-\£¤5B}Ãm[Ìù¯ÿªá• uÚJ1L¶nc‚}&âÑŽÜ8ë¸a@QX …*yi²UAõR"T‚ë’x7Fƒò×§¹„©î[²†9›†¤oOwR¿ÐX|IÓX €MrÓDÝ@˜ S œ‡Šê(楅¶ [û¾Öa+ƒ»_’S`}hV¤.(¶ldÒÈŸYÍØm™°`W-_N(9\@¡¤ÒÙÓ·Z +ìÅ0î9ðÊv”|²l'€½oÇ+8¢H·3NY‡¤hÔ.¿ôJ[¸¨Ð**±õèÅìx¡]ÿž¬#mÐJ ‹YÁoöž•“ö± ˆÃRé ÔÖµ¸I«\’GÀxùSŸho®\N¤z'ÊVVkp”AØX›Ï:™xm£VÏýz98›øS€!vMA™”£ï؃ïiöhרµ%. Ø…‰ÆÒÄ9ÕUU·(ŸG7UF|9c9Óøçª@éÞ»ÿp­ôÔL@ïVvX]¥ÿoôÍñðU€þÍýÜ "Ы‚¾ÒŸØtÕ[ZØñebµTë½o˜ÁAŠ?£g£¼¿2Ào¹ Ÿk¨cuñ_.c%äJ¾j¨-åG@eÏ>Ð-z·›1ÉÎ6[†AÊm3y¿ 0Ù–Âva¢kà>-=³'@É(Eã†2(c³‚‹÷·?€E|rcÂÐzΓøQ{¦;y™‚µ,&jŒ0þàΫîC=¨“"Žó‰ù׋"I#¢¶Ý{áâC´Àùho´=›öá‘_ùb¸×£XVXCK£ ªy˜‹W.#\¦UÔÕáG.ÉÎ+£ÌTˆÖÚÂ9e–ŽIf!b¾S—l°™Ãéöçÿæ³Ö¶=öƒÛˆÅßV+?s‰µ¼Œ¦›’öÙ«V¬b@åŸМ>JRžø&$½ËGò>ØÉ!„h×<J‰FÙ?ÏJ*ˆI$`õ=1’ƒ4vÝ8üeñº¨>Á_|™º§4~ÏkÜœò7z΋øèñѨáÒš„îk¥G­{ðh&~Êè'¬ô_b_ÿz˜Â¥ae•^?  W55¹ã'¸š%@uìÌuˆôøŒVx¶þ(Œ¼š¯ý3J> ä«èÞ°l!ùn€j% þÚÐŽÐ5N}<¿üJDðŒkTSì¢K`>K³µ’v¡ÿB‘4xbxÉbßÃX±%`Xª™),T£’º<ÂþçŒ{ o›hjøã“ý‰ïŸ“-Ë«Í÷ õu5³o!tmFêhrÈ*lˆ“|6X‡Zš¬wU-ípWcï%pÚ‰¯N¯?~àyWƲÐÎ^³ŽØëT€µ;!Ù·Æ©öëég­'zL6 BÄ¡Ew@Ôªúfô 0Ies<S¥fË&[R\ƇSq:ºWípÅ;à~çý+· Z> 1o{ó[¬¼¼ÜÉáË®¼Ì>þñyŒ»™:z÷ x$#z4?ì‰+ØðZ‡ð4¾à¦>€Ô±ž7øÀX9ã/Dß¿3v½3vcâÉÄ:™'Þ›˜?¸ ¿ôÒ»w+; É^ówãC×à@çkøæÿ1ÊGŸÇ‰5uXÓÅWzõå$Ð ÀU{MlàÒžƒ'‡1ÎTëG‚|_ŽêVQõmBíZÀÿ¶þl¶–uÉ ?ÀK7ÜÌŠÎ6à´3aQ²õ*à<ò‹W Ê´D"Ãú .Í9JÚ•™w#Ö>I‰Æ>Y¼¨!™ÆƒIxQ‹‚$2c)ìV®Õ) ¡^¶Ð+Jc£ Rpó„ÕOÓœ‚ Âðë@ÞÙæL5X›® ';•¹Ÿ€!ÚïôA:÷íæ÷¾ÏöÁðK!%2z¿p=ñ@G{+§°Ò:ÕÌžaypÙӡߪ«÷XK=&ÄècŽB ˆ´J…³ƒ'"+)’»)FžB?žƒci¾i ­}ÀJæÛUoºÚzÛëQv‰Ù¡{`B&Ûõïz§]qù¥#váÒR´ßÂíÀÑ;)1Ç}Ñè1>¢èÒÇ2³hèÆžM,]“Ñ_k.¼êOÆÊŒÊ îNý剾ÃÜ‚¹‰)zaâÝ`.F™©¡Äu|AŒÂ‚G7Zåee7ŠÁþëXéÿƒŸ¢ÑùYVú׈ ƒ¶Œ=ï;ÐGeOþîÄkø‘’jŽ€Ãvüèdù¹Þ]ëo÷æ,6ûÆ­f{vCƳñÿÄ›Øfñò%瓉´ˆçÒò3)é+ýgþDQ PsoÑRŽt;4{YH‚m‚j¯¿¯~ÝìïK0NÛ³-UdŒ±Ñl©"ÊsþP`û/à—‹<ùÏV Œwí=aNK©0l¥ÈFÂS0=‹“=¼ šö£}€=3f)ØÝ2'&;œ C¤ÏÀø1ûÝ·c·Ígöîkƒ÷ný;³[þ$ÁÂý ÎÿWþkÅ#èÎ++(›ÂE@Õ½MÁ1KmXƒ°i|Ô=Iƒ&Zú'] À «äƒœNÓ¯Ú=Iýª¤nfü‡½âì^‘z€õÄñtñ é#‘ÉVb²—̲ǟxÒ{ñ)+š1 Ï2õ„ [FØ0‰º;íÑŸ°mûv:£$&V-m–LFÞ…sQ©”¿µúª Ä:­–S:ÃÒQ.B½»kt,cx°ÉbÛ ?€Èâú v`Û^ºñÌW®\‰“‘z{êy•æÛàÎ{òáG)^jï¹á=äá}Œf^-¹71ý4E5KõõÈ«¼Ásq\+¯ž 9D“˜ZÚ7þ›þiâ™ýçmf—^0’Y"<¡{ˆO#ê‹8§PlŸœÜ×»2$oF@)¨¥Xƒ[N'¤7¾Íì*$/½ˆ†àoÌÞÐÿÃ/@fÜrø—/ÀG€ŠøÑwGìó|ûcŸ>h{÷×C hŸOÔ)L§e,²^¡òbè¥`æC#ˆ\„²ÛxKüsô‡Ø“ø¨`_åÝ5•ÖMOŠ œžÒÂR¤ ¨SBi5„.·ÑÒe"àøPB0"ßh ¤pQJZwïÚm>ð¥gåÙl^|i«ýø®GlgÃN{â•mØ¡L˵¤áT¨¤Œ”I¨«©kôY‘r©ß_ãêÃkN_ RPŒAb¦Û\<Û&³ e0IûqQ=¿t6.®r­©B)Û€äÑ4[½jæ ”9 ‰[ðãŸýÄνà\»ìòËqs˜uª‡ëã1$.šè¨™GÇÉ3Ý{ |q‚uˆ î}äÙNæ'ü„&º/-Äó t2¤¯®Ú{¼oŽnK_Í=8^#å¬ô$<×wXéïÖ? ПÅê„Lz<GIšl‘ã•|òCÖço8×==Sž¨ÕÚÆiE¿æ³×¿!xF¶±çjRU·ËÏ-`/.úå—¸©û`ŸŒüH÷¿¾Êw*q`é,Äy/ò~|£==0ß]ºÏRëø;#¸_WÃê®ÿkfwSF>^Í{jíÎ_1¬\Eó1ûmâ[‰H­àsIä#ú•4Z°j_)5,Ù|±@R®ZòR8Fá‘´¡ÅOá€Óò«¯¾É+Žjc£”ÄúóšÐB»`Y™)t7Á"~ö‹_{¤\S¾°Üî¿ÿOtô€}ø£·g·î³ç_xs_xƒ=pàÀçt]hq¤'>+qöR~)Å1E;µÛ^€ÔJ±ÓñXºïêê±Ón÷ÆS0Ὴ ¶6„_ ÐRø‡¬ôèl:ŽèƒJŒÿ ¸Eª7EŠ/p™q¤v.ö¤6ðsà¬_pß_| ôÄçµ!V~åÓ‚Eس_—‰ý©gQæB³§C&‹Â(†P<‹÷Ö­éâé•­ˆó(›1w%€Y„}êÃfx8³Ÿÿ‡Ù{>À»”'e-¥ÖSmåH|(»€VÒÏQí¨¥Ë@£Ì|ÓkEƒQ¶¼XÅzB޾ïG,† ²3áû:£,OÁ&è€H (z晆ÃÑQõ‰ÀkJ0pïCû‘•ˆH!Ò¸ƒR ?Ò!Õñ‡Æ·öîÝËd˜¡Í<ûÙÏ~ Gþr»þÚ·£Ö»5…V×D y ³ VúµXÉ©H "”úŽzÈ<"ÀâݶÛ‚úC•V4/eŒ•`öF$¢¶­0ÿpb‰Úqò"Üd±…HĽjðèƒÚw}ÏýÙ&ËÁVéœÓÖÚßúže•—Ø7¿ù {á¥ìƒú(bFMMÕ:R&£¬•N0 CŠ ŠV=/FÓ‘À„»Á&Ì… ‡×€jä¾Ð1ç’É ØÝ–ž©KÀKæo$þÔ*äôç2.§C°-çYÜñ­¯ê^üýÿxLwØúê,÷Œ3^üŽÞwx[€­Ó߇þòûÍf›žb޼Âêþ3³÷¿ÏlûlZáIùßyä>ÓG?ŽHúhR ÀªôÙÏÎMßA× @ÕŸü2ìpùÕhóý”ÍN+úH|¨‚ïþ—á¢Þì…ÇXÕWzð¡0zÁ^ZÖÇ*3<ªž)*”ÔÁžC§#ðJdŸH˜0çˇJHXè $†8PÞ‚½ÁþPoãÉÒ陘5yæèAx1Õû5HÁt‹Åúk$îÅÒAXŸ´[¤?d<€oüI7ZÃö»ä–f]:ûõ8)O?ý´»ìš<„ˆ0 ?‚¥D©p3J8¬RÒhÆ$wZOŒÀ^Ì[kÙ´¦¦¦ÒÚ+`úÍšc+×®p¤B:ݸ¬^·aƒÍAðÛ‹sÑìB ˆqJù k:ôеBÂ¥†Šâ‹VÛª‹ìÛÿõ-&ÃïíÝïºÞnùø'¼Ï"æ`T÷T”›N(ùxóãE¥%Sá„J{‰RdS •ÉúÀO@ÿ"Xé…D×BÞBèûÌwáß½‡Eþ”È“¼ûèQO5ñ"P`É&F½'T§?.}ï¾[’àÏ‚j«X}çÁ [PÒîc·‘¶öt³ûïåEÒË[X™™"t„Î8’þrèÆ/îÑŒƒa`ÿè£6a¶ <ØûK}÷ß5{¨„ÀPÈòïà!¾’*™Ã\#ž…Žê*(Õ½LNÄüûóýøPäÝ~TÜÓR¡Pµ©a¿Œ/çÚHžÈ-àxÎU'!­ìç-›—ÜÕÓIëƒîlz€÷ {\ƒâ  ë?f`ÐLDù>“ÿ¿$VÚD€ú@éÉ'Ÿ°Õ«W³5èÆ%—Œƒšëu—½ãÚ·Øì…kmgmAfÚŽW@¬Ú28õ=íðúÑ-˜µ`.ÅNµ=‡B&ÒªÔªëêñì³ÃæÌ™i³ËØ0(ZWjwì´fxô2Š,ùsg¡gppO£UTVÁ0¤n¨Ýµ~:OÎðI©«¬~îAûÛ¿ùˆýÓ§ÿÑ-\låå(1HéØz§óÎ0¦ÏîÏGÕ_{õÁ¼Ó寞uê*àhI”òèaEÓÅCMrçÁU8Ðøg€þØ.Ýy#+þ2êÁ”pq]¸Õ™¦Õ>¾ve Cq>«ñ¥oÀÀG I©?Õ€~Í~üCq€V‹¢Én_„(Gm)ªšýæøëû÷@«O¶„5ð퀬ÒwîØ‹ÍžÈßv#«ùf(ƒgùIf½bIi__¢¼Çôs› NmÍZ‡JQ£¶ún”45„²/hl—©f}kÉ‘¨Po‰­?L@r¨¯ù/Õ`‡m·ù¾|¢77ý¾U¼’>?É«)ë}34ÐÕ†’CÛ8Có'ãž[V$§  ’ÀIîa-úúl'Á<É'nþÒeKüüÛÜÁáynû.eÓ–‚±M²$z3‹Õ»g]èxVÀ­®®;ˆ“K­â¨ Óé¬òÛ^Ú ¦¶Âù¯ìéÂÝuž-š‘cX vaq“Џ±©CŠCùsr` ÚB‘‚ƒ­˜Jà ,Ó­ëÅJ[¼`‰ýø'wØ—¿ô%WÏÍ!”ØÇ?ü×LXÔ>öóÆ‹ü’Žšn$Ý º2¸>âoôîá‚×Ù¦Ô©¸ÇJMFª1x êÏ_`¥¿ _Èç4ñ´J 4I æé ñƒÂ&þjÂiµÄý ëå¿ ÜôÈ m«80è+ª€Yùð gx·¿¾Õˆ gÿψèˆË @ªÑÚ‹Óí¶õe¤÷"‹ÿ/’`ýØ)P ¾Šr@4] ƒÓ×£æ;ÏìËŸC¤÷ ³¯?ÁÞ8Uz…í…¼úÈhî|°”¢W€x´­PÂxÔ©uªçIíQ¯ ¼Y‡aÇw؆RN¨æW20ôCPšõé ÞÔ1§Øœè”¾¶È>ÞžÁ¨È"6ÔMpTj¡4a’·Nò7¬ÑI–røëT^Ój½ÞQÈ]Ò ÂtÌÅD¸ì"mtÛ)‘¦$ÙD777Ûoû^N@ïÕ±‚ë^Ö‚uu v_7ÚlË—/³'_ÜN9¬lpèk1ª­xÁª:X?Þcê01FŸ?¡©ª'Æ=Ÿh®«µ¦ÊF[²‚ƒhh=ûè#„m¶w×EE6€~AßNÂ\aZ{J6!{¸­®Îªp(ÚŠ+˜ƒXþÛW¿f30 ÿÞ÷nµóι5f¼³ •'äq€ö¦õgœ‹M+:F¥·¢[G-aªLz›¥LÁ4“ð·˜õ¯ü}ÎògÜ„sšå”ÍÐ;‚‰zòOCÒ—Uª×ÿåéKB”Š=àú/TGÀ¬¤wñ•áŠ<—]ðéÞܬÀWŸŒq¢¥«€gÀq&b>V_(pÛl µ'?}@yȦÒl\ú¿ÿ¸ÙGî€"á”R‰9ä‘£j¥ÝÛ˜#œËó¯ZÖ›÷ý!z~^  +;¯‚l’Ús¤#Ÿ¿âk”ŠÂóry#ñŠs¨9îc ¬FxÛ9#Œ¿>Žam¤¨¾Á ö¨zÒ'¦5MnÃtî­ 1)AŒ@ Ûéa2™zùA .Œ`›x£™±`½cÇh’Aÿ:/dÖv`Ö¬Y±g Á>~z¿íGßµwbÓßý÷ÛÖÚ=¶Fß¾½ûQ7M·—^~ÑêjÚPf¶àìb„ŽOÃLwáÜR›]Rê^†·#%˜‡iñl êkëÜ\7UÀ7ì]Ÿ¡¥§÷Û-ƒU?ña"JßÝøLÅevÃþZ‡þí°j[¿ñ,{÷»ßm 0)±ë•£L)wHÞ{,Iý¤pìu îžøy\@$Ãý,£IËܪ­‹%-GNïØ"éŽ$‚OÛ¯J•^Ÿ‰kÑ„ò%|QË´·®e‰ûãP¿'` ÙŽZ‡Ëåõ²Œu”D¨äåÛˆf€”ÿ®¬£íÂ* ý˜ÝûïðÎfOΪ/Yÿ'¾jvóí›ÚR _>ïôQŽÄuÚ(‰‡«º«nà«¥àoþ/²þ_RÞoÌ^|=„çq Æ7´ýO†HmUF@M·ä“¬Öú7V§0¶ê{§ (…Q|Oþ²òˆöFFš¹¥y×y¦ñ÷„f—WžJè8åŸj+—±¤žÎÖÝ\ûÉ"€‘Å”ó/³°Ì£Ï9ÅoüT'ËIÌ!%é¬ÇáG'£¨¿ššÖÓ*oUe¥]vñ%Ö›9b[0ÆÎÏõˆB¯\{&eô@î¦XŽ@ëñ”ÑÜÑî¾E†µu’ºbÛŒ`í!T[ˆN@ zŸõMÍ1Û"Ë9¦|¸ãÖÔr‘Bô ß™ÃàutµZTŒR ž!“;ªˆ9HèlìJK‹Q±É¬øÈw-Â>Kü࣯³‰)"uWùÃþ™˜IWz¼ Í1uyj:á½r™ô3rQ ÊAmcHN/f<ûáÅÇM$Ø\–˨¤à÷¾Ëì3ðq®{zM6­Îú¬€LʘÛ!íáùâh<æ€ò°#4?OÒ°•6dvÁëл7ò÷YfŸ»"`®¿9ÿ—ÅiéÝ{7ߦÝËV æKVR-ñK])HL;Áþ¬Ùè<….ÂÏÍrO[=ÖmOóÂÕÁ’ó% " ³L8‰)ô›Jˆ]ɇBÅíW Ì @õjlÕP1ÿt ßç,&a bçŒõPk&Ây„¢“ãL|ýøS´Rë8åEòH­EžßóŠòPéäDâ¬%§¡›éTèºTÐ|Xpo´y3–Ú²s‘3§à,‘üJuu5.œ›VÊ·ß~»Ÿ~æi |r™8;ìÑǵœE„ë°\Ô‚wá ã™gž±ô²…¶líFklh†¾Ÿ@­à¡dl †áô #ÍÚÙTñ¼Wa…3J­ÿ¹l²‰ #ã2Ü”W‚€„‘‡þÁ0Ë•‚IdÌH9h*²€Œ áGl,Xjüé/Hðt„å—¿ ðU’²ŒyÀ{cì~<"ñÙ0–eÒ‰ÞàO”V„E5 ìée„Õ׉'%¸Ï Ð*+Ç vÁ車ŽûRƒ-Àä„ÅÙÞ|•NHªJüDS^í³5"Ã4«æ9*< æÄ«…†sÅ›‡Iu®¡ò­O/’Þû7f?ý\{ME¥Ù?ÿƒÙû>¿àÛ(ògÛ!é›ZøûözHsijuçÒ¿-`U+±º¿„ˆkQࡱĎÏû‰©a24R"[Øw~9áp®G0xÓÂ-½ŠÂÏX¢ÿU€kÇÐN=‘ç¿èU>¤HÕý²I"zÜÍ©àÍŸ†0ãYã'®V¯þ–äïJÅxÝ‘˜.:ìH¹¹9ÃüÑÁ±m˜ÉƒÍˆŽOVvB ô^âLè9IbÄ™Ë$¬ÔòóþR”LiVUEüxÄ‚JRÉU’ÿºçP×Úôô&»ã?ÁGû\[uúùÖÙÖ€ ìa»êªkìnÂzWì«d‚¥"ûíÄ‚0ÕÖ”/v®q ®xª;êlåùo´™xÂÝŠà7À$ù³ËÐçîD5t¸„x"&úTŽsa.›]îa³21åbɹBÝݽ¸}êgÕ˘]R€ÀpPÃ9á•~t¿ÙE·Ç¦Ö‘tª²ÆgNP Ÿ N:ŠWÐÙ:€†%–˜¸/i9BõÁ?ÒwÆ+ôªg*G##²_¾ð®;ƒ Ꙥ-âa7÷T%©ôvÃ%fO? iý”|jt¾Núɧr€r¿½ªà¢KaBÔp}|o,A×_å“yyý7aâÕ!ÞÍî:÷x+ç;rÎqëó0Ÿdå>Sh1uQÙ|Æ)..÷Û÷·Þ 5Áy'÷xÕó9žá»"ë³Ø3ÔSµU¼#'•NócPîI ”Ô:ƒÂe=écÅ‘~Åë“ ¦ …r—ü+(5dÑ‘yy ylQ§‚7Á¢`R)‚ѸrŽz*xÄ$RC) îºÒM7Ý„‘Ì îÄšúÃð‡ =*&\ü˜öð_ÿú×Ýòéo\wöKškžŠÍ^C¬ªl¬S(%ØëËâ°áC]²þz«Þýuâ tÆŒ™x™íرƒÜ­ è°å=°õb2ü‡?Þc×¼áR+™¿Âú†ÿd[·¼ˆfW’m:s‹vÊ)öì}›¬?™i„,G¡­µ×¶WÛ¹p`6®Zgß}øAKïN´òåKmñ)+póU…WT7‚$~ 1{…žÎ&b váɨm´T´ËrQI†:hÀ¿@r¯­E2pOm cŠìW/û’¦qœ¢¿¢[>Ìz0¸P 8{'þ}Să®…3‰̼Ë`cÞÅÌO÷})Y2–“K=Úµ¦6Ž›­ïõ=c{x¥l…ƒ S†ÒÎsÁö@à‡ú©±3ôU¼oUûCéå‰é¦eLù”tTÝTÎx¯qAÒI ñä¼ ¦‰‹Ep¢2”w<©ÒH™x g4Ò_МаòØ=4éšBœXö›þ¦?gÁ—æîﳚ_z¥ÂòˆŒ5ÄŠéž9'þj1‰@³GƒU½}T’‚û-ý-©äGÍgoZzzfbföïhß'Fc}C½-»“R2g`¹t†ðœ3 ˜àÌÖö¨‹V_iûPXil¨%¢x»ôUÄ(á\á»Å&Qª¬¬°{î¹ÇÎ߸†˜~ç°bçØæí[¬«¿\0fΜogž}:”1þîYfš‘ÂÈ«¨Ù Up%[3<÷Œt„à£Y³2ñB´â—YÕ¡z@2MÕÎÙÏLKE"Ñnͽõ6V™••γ’îT¤ aÇ,L‡ð#àJéКê7sÍ$ °’Σ¤¾â}±_ÂTSU‰Þÿ:{ê™—íÅ—·ÛÚSÏÄùG·ýá—¿³kϸÈÊ-&~«‰Ëi«¬çþV§B{™ØwÝñ˜e•tÛP.Æ>ˆf*jÙ¤[ù¹+,g°×öc¼QW B&"ÂŒnHÿJê€l…¤†(ÆfñÜBG/ŒÂÃ3ÊÔ꽂F±³³m!ϸj*¼ð<ò„¶eÞ0ñÚ¼9<Ÿ*á¶÷ ¸È‘Ñÿn( fìÑûMâ<¬#ˆ_Ýõ7×Ò pjW çðFé g÷;©³â6Ý 0¨šy„ÖlMˆ”ÛÓÑ`ðX?*Ks丒>ðj¨¾W†üI²ÁÊçÑy~‚p qpñ\_O=”A(»–#)GH[ ›×æQ|0óíηíUA5Ý£Š¸E$aÇü|X¼$aEñúYuï»÷{Z휳‰î{éK))Àa(…™¸VŸ½b¡å— °“hóçͳì…Xb!ÖØ€o·^+.*²&Dƒû¶Ú,L“×®FD )Ÿ‹ëïØÐ=Ý=ð2­ ߆ §Ý‚îg2¡Ébx?;T_eÅ8!@¼)G$Ðà{D„KŠ:(ê¤à῞alܧȠ[S”¢ÏI©+Ü€ë"Ãc*l)Ì-jxläŸ>qŒ)ø@ (l–æv7+©¢ø^Zm X¶µè}ÆÄj·ÿ7|ž-)7»äõH¾Èÿ¼@9è·wÜ”¥àg±NÌ%ŸÄo¯v–C¡’ÿ¿¸-P9ée€çxWÇýûûþí†Þåžê zkâ5Ï?ƒ“#RL!%êeº¡T!ÒÅìCð†M¤#jP4Ÿdþ£NÐ:Š,sŒ0¡V‰¼384Ø×(>Žª¡þT:ü9Žý÷¸À±M%½º˜Ïúž¡»«ù—Éœÿ$ Mog%"PŒ% ‹Ü<’’Î4ZRz= W_(e-µ…ëÞvTPùßc{ርXý ƒ]{˜1Ùlîÿóý¨…²¥¨}•càÁ?þÒŠƒmÞ¼Õ8ä+…Â\Ü»&a5!”Q‘¢ÛVÁùoF±¨b÷>W^¹n9Zdˆ ³Q8jµC5õ0cˆ[Z†äU#8V}ð J KÜ¿`¿lÔFj'~nð/˜°º÷ªIHB)ðà"ü=ìÞa7‚5Û”˜XŠð#ëÈtìÒÅýo'ð¢(^0MÇ$Pú¤Œ®@ÙÝ?IÀ÷oÆ~þW—½ž!Ö7µ¿ÿJ6\böß_‚W0ýû{ÍžcÅ÷l Ýí{{Qùââ‹ÄWùú–˜„šX­ƒfŸþˆ‚¥Z÷¹íyd¡WÈùïw !ØH&„µJúÞÄá@¯¯‘èSOؾ ?³ø¿-)¿ÊÒ2ç[jR¯ŒR¤ß¸H#ÂÕwÿÎü÷•œ…Ï™3aQÁ Ñ€@=±¡þ¶”Èác=–3øèÉý†5?¹BŽðvPQãzÞÓÑú;pA+§BÐ Biñõþ®ZÈ´.oÚ(‘S;B!(‹ü¤¥°Rã`Ô K‚ÞÊk"aµ¯PQt ÈB’‚/~ñ ~óæÍ6ãàùqYòÆ7¿Á² Ë-kv1ÛŠl[µö,· ,(™ÇÖ€8m©Ø¤ÂØËFMÖ‰ƒ¬”˜+Cï5£¥ÜÛª3Oµ‚âx"–fȸ–)É‚ZhnÃKQžeÍDmyÖ\ÜeâÉu@LžS“pqãàNoÛaà—-ÈpØ n åD_öðlˆ^‘=à ÈLÃa*úÿ)v+n-¹1ŽtîûÉL½«ö:p±o€Îŧ> Sð ¸ýøÍXÝ—`|ÃÂøÅ¯a¶{Hà·Øø“ç/¤@¬î)Í^µ0ÓìùçþH6xÁXÀ¯¤ïýÕ_">w¤j=¸Æƒ2|~õ—ÇÆ‡8©Jô¬æš³Ãx´½Ïbi°„¬g¡H» l$‹Šô^’Ù:¦dÍC*5“­. MbÛ(Wé „1¢ Í`þ w°€ú$><[é¤rO€ÙÉ{ÚÃS .×.a¿þB `·eO=xû ä£®âtmzRøP«jwÂHmÛIÂ2Ò¨Œ&-[®É:Â=šÐF³]E”‚ß!/™¿'Êuº˜,‹£Dx]Ã} Ì­:„6Lo ¼×ø8ú÷úºÚv{ðÖ0Çô4nb)êû×4Až«=j¢‡´ªÝÏ>9gÙúffüp¿_‰hþ°‚ Θx5ÀY¿eæâ“Ÿp…UVâÀ‰H*D@ir)–˜·ÊŠæ·[ V€¾R~(¹ûÅ]w93ðÁDz™ gãÿ?Ë~ø“;Q,*¶õÎ'²Ð*¼þ $R]myìq;sñ\L„´ÏBã«ÆæZ‘奢ښ‰{k‚;摘?ˆïÀ[wYÖŒ…V¶b‘U¶ï´Žf¶#%"»²âŽ2QÓÇ—‹Ã“²¤7$Tšò­)‹™2g0§(G¦î}†w…ð—Âj$ÇuHnÿ+ÀZs#f¶Y¹É¯5¨ôúŠî)êR3ÝÇ܆ˆ°ËËPây‡Ù†sdÝoû Ù£=˜â> ‡þŒ¾ 0——;<ºø*4/®N{[€˜ÄþùÒgýƒ46¤¸ŒúJÁHH'êNÇÒT÷Æã‰6lÒTóäÜ]«w"Þ¸’kð¡‚÷(ÌË“œºc.À¨î$§yÐA¶D}ÃH†`³~ÅXÛØßb HP³VûVÞr¦ ¶Â”1¡ön-ÀÓd)ád§|RLΤÓ•^s’çDî{€•´¦êý?š¿l]1N¾ §³ *"”¡¦ß%¢êÃóíNÍšA?!W‡/ N¶t èU:—sf1ÑRˆ°‘ë ¬óž _àWç<ñät<$¾;3ÊQæÅtÏÀ³pÿÁ›ÞlóËʈ&¼ÞòcõVÕVƒ£Ñ,;ç¢ v°iV€)V>kžUÊݘ;c|¶3)R¬öÒØL[sêÙV`sã^'­úK̦ÆWŠ^–í`’¸’H\)êÁ(_ÜíÃNÉãÙø‘FùWB:ÒÞ„ 6ýê® 4‘× > _Ó®¸ Ž9ÛÍÈJNÓR[ÓO毺†ðñ(:*|=ÝñWsèðUÈ@Ú» _×¾ÒŸ²~»)@(syު͚O¹À¯¸Š|$·—:°ÊýÄvÒäû<3SJùœ†äøš$H4L™z|ž]#aëF¡³ê‰`Ô8s”ÙT¥Vñô¬AëDa,) ÝÄ«©0PäõJQ¯“Xñ™Â6†Ëðp sOÈB‰Ždn££¯éoRÒøc7ÃÜíC<¾ÓÒDÕ ¯Irôòš”1èrµMìiN?°ýɯuw·~F^Ê4L-™úG[é¤bôwÕUŒí¸A¢=KGz×ùæ±"jÄòT¶òаsƒ ñHqhžQâ.))w@†QL;^÷ìÙ岨ó.|n¢P]=’ÁPfv¶­¿ü<¸û½DÞíDžL’äl¼hõÁKè…ƒ›bÕµM¬t(+áì±MÅÔÒB[²æðCL+„0?ÆùÈKÝ;Å ‡}3~òÈ¢Ùîˆsl•sNYTÜÍèTóLŸæ|Ò“¥VJ1ò¨C´5w™¥È8÷þ.P¶É-䙲óŽ`QŒ·ˆÄ?DÅäv«2?|¾Ùï þù½8û?úR–Bâ×GÐ÷ûbú‰¼— T¿[ýéÛø`±@PÅ]‚ïßÏ*ß¡ „|¬V}ñÒÔ”iK2Q‹*P¥¹2ÒbiIÛ,³`ß>¶Ll=à3¥Á°š/)E$>†d¸©ON¤<™0U‡P8“K`Û"þÚ¦½UÖß¹‡…ì À¯F©öþ5Žjq8PÂl: .Û€D”¢úp§7X+ïÙ¤„ytkzÒkNLª&ó5€„„þØ`oZÃÁ­ŸÍYž™[2ûðÐm kе ­¼¾¾6Úl¶1È,ÙÙc.Œ° æò´šÌµø3Êci˜a‡¶ÞËëšÊ ÈC[‚&ü:ɨHÈHÚY3ñ ™†ÿÿ¶úCö‡ßü’(Áe}íÜÃ9èpb}çŸ%Eùìõ Hذf%ƒZ! ‘§g°­K°CµU¶ ´ŸÀ¦mÖV¹Ï Μc+ÎXƒS‡ ¿ŒKÞs{‘pšpÓž&•9éRŸóùÅÔ‘5œ¸ÿÂGø?±æzöÖܬøo‡##ûöµ¬œ9ív÷ÒÚ«hgÓåoX€cΞv·½Œgž‡½ýY?s81…ÛZ W¤mçoýd0åäb j’õx X>{éñë#íT†v"[Ø&|ö˜€Ü+çEm }<*Dò{ÎI%€Þ½48IÁü@-É óS‘ÙgƒÜY0d‹Ñîñ’Ø6É„ù„ã™””îg`ÒõÄ܃³šH¢Àþz¨¬?+~ŒÂw zA€O£B(î…tûð$O¢èk%>É#ábfR dá8õ<8ѤZþŸ&€P’A¦ ¾»FGÒšª¶ßPΜûO8é‡úGz;:lye![³Lª 1&UÜmN!;ɧ]mû7ÿŽ@¹ì®mÀÝwßíÈÀWUZ«à¢©©C–¿è“¶gµ=ÿìÓvî¹QQ͵gîÿ ®¤úìÚk¯±48…³ðPÅ Ndÿßõ F¤,ëáæ*®AOw»=óÄ[xj9ðR¬Þ´€üx×ù€€fQþjÍŽbæq*cQ©åj§ÎñeK³—K›-*@ ïl¾­ ^]Üyv÷ÊUÊ#€ó˜rn\R§ÐP惪‹µÁ—«fhðS’-å‘{™C©>iõ½ŠéÍüÓ–Iñ%àÈŽD÷¡í (“"èC²•ˆ×ŸîNÆ™“À«ËõÕj%jøí±£îsO·U§É G@Zàðqù€i¹œœe:¯ÿÏ@„ê ¤é-Õ;>MgõΘ÷EVuîÉU°(´Á¤C0W:Û* :¬‹š0LB)Xhu×âªwÂg)eãümñéo±[ÿŒˆEºxbÆë»:B(]jo¸òj{è·¿g²öÙ¡Šjʿǿÿ-V²èGÂé¯l±þ],Ù˜„u$åÛ`sNCš,©9fyøLI/µ–ý8éî·†Ê6Ì6=!D’ýø8ûïTã<Õ€jÐ>4œãócÚ pÃÀá(ÊjI8~•+bu²Ú·‚Tw£ÐJ¿ÂÁÉçjÀ0}U5ޝuÜy°20&— "PS’ëñ£ñ ªÝ683º3½éÿÄU_­ Œ°ê ìüWFb¨pÖü¯ÐÉB ZD‚íq$ÐÑZm…Eó  %&äþÚ¸¦hkÀIo žE§]i•;Ÿ‚´¯tJÀ‹ ;¼­¥Ù¾÷ƒÙMï{/3=ýÿ[P^n^ÙlgÎ(³7]}•ÍšY„æV ~áÐRlϱnä¶HŠž§C*6౸ŸCKhìõÄÞ&KÇ‹dSu/«+”ú°Š8ž7eâ„àV”z‘ÃQŠ;nMqŒribq>éÒ_ ÓLÏÀq*«¿¶ê‚löû(?Ú‹PªKÀ וC’3Ï»`ˆï½÷ÚO@°šË£®tðqxÌ6F 7q_«·Vk-ê¸W´ó.æ„$$v³_«v«j¢ýëgqÒQpý¥ØCÖ@ƒ#¦Îø€_ýN/̾¯§íÁw©qb5íªÅ * S°DMKĨ‹¹¢\©ˆxÓá8v¡–’ÏÖ p‘…? ›þ^{ߺF‡ªØìeîA!à ëíF?>ª¹Z¬¿É‰Æé¾©ƒ&dñ“Øh¦wú`ïo»[kšÉ‰„,,Dcr™'­1øÿ3y¯H !½¥v׿·Ôú$ŒAþ#Úÿxw‰ù'J@²ÕÖæÌ(‰Y´'cVª/É%æ•hOR°úËD6¿è”ó,³x1ýL £ú²Ï>ozÓ›ð÷^m|7ž_[Ø&Ük¥‹–Úþêzûß_Ý…Öß fcŠÍZ¾€…øÄV _T½8ÿÐ>0å &Kgñ¡«{ÙØ2pVVBTbø²åÔù¶Zé):Ñ1:žÁ=Õ1¸Å¯&Ô‘R”kb9ž›G“ï Ù8Ònì;7dãxÐaõÞù-·XoýW€¤ð‡_ÃÅ'¯x,ü¾²Ï™•@E…™E¶çÕµ|›+ûÅ¡$¾•F¨;ȧ¾E9²ßئj~h’ΧZÕ•7J]õ¨ñÕ)Ð4¥cu|mAMûY"H²Üü$LÈ Pë΃¡h½ÚÃSQ¶q©pÚc‚å+|Œý½¼('2ÇrÐhJ…jhk|„¿ûqÿU/8FÔ Žxvcã‰WeüG’p=r‰x82á!ÌÄSÁÿH2žôbÝíM?Õ–n9Úß>‰C0ïN¢€ixU]¡í€S­u»¿ÖRWñ±¤ hîÑ9Tӑ޽‘a55í µ[11+ «ÐÝÇ7t K›¬ S±ÝOÃßÔò5!è4Ÿ â D+ò“O>å^Rˆžž^œzÞ€[)~ «d‹ñÝïÜf3Ë—ÛœÙs-…e±»«Ë²gâ‰øQ]ýÝÿ_{geçYÞ÷ïνsgÕŒ¬}—e[rl¶8±Á¦ÐSc0¡@mO—š&)í9=É ¤ ÐRh—S°‰ 6YŠÓàˆ0Äk€Ä°ly“mÉ’%4ÒìÛ½sg»ëô÷Þï½óÝeF’-Ù’|_i߻}Ïó¼Ïûl/*E"ñáãì= ¯þáTšÆa¨ç £"kpCÚîºQFmÚ«^Gÿ¸ôAº8u©Hl$ü£š#EZñ¼c0£!ãDönWÚ9›åÙvQK[3VœÀëþ;*Àÿr+|ëpÌù­ ø÷ôL 7Ly]‚!¸…‹X—óîXAXx.} ÷¼$^%Åï9èÜ{e^ zÙp°²–qÔÌ/”9^:‘<éýŒïÉ¡ÖüYв|71'áÞøÄA\äqØtÙ;ƒµLk’šOÂ"N¬½“™>K¨0›'ÊÎ,êBŒDè ‚ºUþðbùº|RÎà€\‚ÁµOËÙ"'õs©{Æu¸¡ !°šE~2c~úÍ ©àáW/ÇQ‡¼’ø#Ï Æ ›ÐK‹Ñ§{²ø›ï96þíÿÁ!ò‚÷qçGð¯!¿ÿYÞ¿ÄY^gÀJ(¸ñ6¸‡ÿçkŒ¸¦#ݾÑÜ~å©Ì Ë\DÌñ-ˆÆÜ¶ ÙË3Asך™väìoˆèUBã6¬ÑÒ¤§AvÌ¡q‰F|cK¶¦'6È3YL³æ5£}÷“£ß <É7Å®o½x‚Ê)Vßµ‘^Øçðß$<.’•5|h-\™¢˜Ádêžü·È µR_ ”Jð…êSžÎ$ Î©³ár Ö–:pëðÑ¿Áà°Ç€)÷í¹a¿ E1˜ë…jOÂö-cYÀ«M½€ºGü;̀ţ®Ã]wÛÛ>€ (ILÁgÑá ·—Ív6ÿCÁC=Døñ4À K  5—^|áO>ëc€µ¿@y„ebÍ80” rÔ»ÕãÈ?82ô ?˜"tx‹$aöñ#ßΦfkô"ŸÔuͦí0ÛÒ‡Jè~¨ ‡üªÞÕ­ØsE–M³„Þ¦©1™¼“ÈÆ`‚4çòàëÜv=áš3|še–LZÿîwè+œ½ÿçÿ¸ø ü—Aî6誶Ì6ûÊ“Þ_£G±§&yÄ÷³})Únîµ?é1H¤‡vÖ÷+ÛÁ€&h_–仉JZ!ä4E„}ò'[4e™ýÙ[’`y¸Ôð3XuÞ òßQaómgm`ë{‚x˜ZCc]9ÞÖ9[Û»q^èlä:rZ~®{¢ñnæùó9ÅÌHõÿœï÷€=*Ÿ~äW›ø„g\ÒÉEáXkf´çÿ1ôs«·\t;^„Z°©ÍÆ Ø‘ŸÑÑž`lw ÜÏ¢ù-CE.Øþ,ï։ѾÿÌdw»àeŠ>É¢¥”ë>E'g"Pׄ"YŒö|#ÝÜšM‹Òב€Ä· "ºKÙâY3t^O°SD á$>Ò —0!^¿~3”7ÜpãŸa®Éz Û£víQU‘¨‘|ã×þ À§hÿÄg>¤ˆøÉO~"øµýzЋIÝ»‹A<3l;ÛÙºàk"š C^ÀwÀ¢¿F˨åaëÕÑšd³žÐéÄS¹XÊu\€*P/PAب:9óDƒm‰ŽÔ)/f™u>#ºì·ÜËOAiÎÊKkz"«¿û;O±NØJ“ú9≻ônä=ÖÿÄ_ RIî9X×8¼‚dŸöj1G9Hè›býàæ1â0Èn áß*ÉßLqš\‰É0Ü7Œ<(ÇÌÞÁ÷dG” ñ1b“Å›çé{7²‘`bd7fÐû‚™éç)Kßµ}|a/cîB¸iÜýا >›|õW‘Tvù®Þ€™eªÐŠ36Ú÷û,w¿îŸ»ÊkJ©(ò^œ”ŠO (ƒO2£GïîÝÿ/¡ À˜‘h=g¥)Áä—Ù>3Ïdj„›’§‹Ià1VpM ²F>ÆÌ.¢ ½í†-;‚ùÎíè¤àà/Z“$ ôIçúN*O!Èrù>÷9³¸óŽoÛ¶_ dƒMxN±…Í‘Þ`pr4èŸg}Š?€8 W¬/²æXý¬z5rÉ÷ÔÆ…vÚ)UEš^®ÇxMCf©9‚çuâ%Á褈S9—;‘ÌB–‹ÁdQY´–~´sa;,è\¡»X.lÚÌN<V ¢Á»Ú‹OïU÷É•²Ø¯ë‹ûuB½æ`?úÝAk÷ϑ濌pNÃÕeù®j‰5ç‚5[V ÝÈ©¡aÔ}¬Ñáü°&gÍ^DЇÏ8û4ˆEÁ‰gŒ™~øè]pA·£~¼“4!Æ;¶\,ÏøÇi¹fz v™õç\·”üÑ]¹ w_C­·r ‘ˆR ‰FúíþÈ=ÈŸœ+{‘ß×(©€ï~„4µfÆ{¿ÏtþÑ5›.º›µY gÏÉ,"Àò©J?ÒÎGOBÙq‚LÚƒÉbmŒKå—ƒÇòå«‚·¾óÃAßË;Y"ˆˆÈ‰GsØB’Û1õØ päþàSÁÿþ“눩·9˜Î®ÄµxŽˆEÓ„ÎÃ}¤°‡]Í;úöFˆª¡#ßx٠꼜N†Ë ›XK(Xa¨%h'HyE&ƒçÚnÉÕM9×g:P RD-Z2£‡,–ò^·ùâ{y†tÏàG°É‹ MÅ}WÁå+Öš*Ð\‰™ðʦ)åIDAT%ÐIB vD/̬ÑÚÒlÙq;å S‚CC”ˆ·Oþ\ÂAùiç×ÿúÙÏr,üÅ/²Á•AÝÚ4kÎçÃEµ½Ã'õ¥¸’Ýì=WްVë‚`ÒUísùVDX‰œFšÍL Y°2#Ææ‚ôØ„ ¦—5o €h-:WqB ì c¨ osu5ÀBT-þ'ëÿËX1ÉKÉ÷Ñ]ïW#QæOuA²YRô³WÌ(\Tn Öêz’ F˜|ìgg¦©+nj;Óì âŸ$ kgY7Êj/þ^-7ÌŽ1Óïa¹óœ™ëj/Êfvy¶•#ß˱ùÏ·ô¨ƒšÖ«‘ê¡:¹dG}÷œ7ÜK\ãøÒÌ÷•x¾)ÑtÿÄèÑ/¤†íÆR“ö¿f¿š¾qãl j·Á&H%²Ý23Þ÷£¾|ñÃlࢠótñ…n‚â#à?'0Mp!òy«6@°Ë/ïOŒ\B\NDÌøEfƒi€lÜŽoÿËð¶ˆj"ÀË ¦c›…hšµÅ1ü·O}š¨mÙà»÷Ýìd£û œ…V-Gý„ß­,…Íj¼6•=KTézä’–!1ò·Ü\R¾âDýYx§ùõÌ‘ß&Øvbã82•˜‰]§ý´Bª—YI¬[›ØÊ (Þ²ã—¼Ž çùƒ(bzËÐ`YõcDÄ–«±Owÿ¾`ûX_vù;ƒxz_°šGûpˆoeQŒÌâãsVk€¯òñW¡K<×£°íÔ]¤‰–:òyL™1•»¶H»|Á±üÕ@’¢áº÷»hž/ú~fa†ÑXÿ²ÕC5 !šg†'Ð×w£jˆgð¡`»«*9„uÐbcÓ1ÂC‘©Õ~–+¨òºÛlvÏÁöƒüÚ\V±›ñÑOî†SO„Ù‚Ä’«•2eJ‹lG|†6ªVKLØgíÒùb)3Uo©ÞXÛ½ê¿(é^ñ3sÙÙÛGGß879°WY@†MjnÍúJáGr¯Ç¯t6¥ðëHΦÞw8¸vÃÖK€£Z{5¡q Ø}±æŠÒ»fÝ…°þ„Tt@*b` FaÂ7(²H¾Õë·X-ÁËÏÿ 9A}NÀ…S"ê©à?¿ú²Ë®îúÖÍÁ?ÞìÿùAv–ù©PÃASè„{ú‹Þ³Q;•Ûí%¦W+ÞÃkÍëªÌœG.NHòvíH¶ …Gþ=WI坨~ýEÛ¨üú“‡ß1 8Ük4Ô€•¥pâ–À æŠÀ #›Xó I‰¶ƒ.N§ì,–uZOð¼XeÁ9KÈ f¤Œm¶)NÐÅrj ®EfÝSè'—­Xaª½©É–4ϱ«ð³”A„&81™Å™ñM†#â-ŠÃ»¶«žÅ’žYË9pn—á±î+t°¼¾×{ó¬(¥f}ßÄb3‘FÎôW£c‡o™™8,„ÄF˜ÊËûå¤.øSm¯k:Û€Ë>Ö±d6=øtïK“×nÙñöûñûe"À,“9pnn2îǬwýùH†Û@l…nÆ$„ :3ËŒwÄÒkæXGà¥oÿgÁKO>`Êž\³X Ü7óS„ {Yê°'mW kßõæàÎv[Li9.©Õáå#à.:#Y‰5Øëê©ý­;ÜZhb%\51kÊ(‹p²À”žc탢ç%òpÖÖRÛã…<Òˆ8Ü„@ðÈ1„† Ú,æÐNÇ@_j'™í[º&Xj°ýûJ¼ïfå]KT$RqG;7cRÏÀæ£jE¦à/Æ›ö ™ãÚÌç¤Q)bÄ´Ä–¥Á^¾ ÊI.ŽJS²qtN뢱qã[—À.tÁÙPFÆË®«3ÙójÄÏÓ>=H npâïňèë£Cý·ÎLôá6h£Ghˆ&©©<⻆4¼þȯ^ž@íÖÀ3˜Ž]è¹ÞýO¼oËŽ+ŒÅ›7ƒÙŠ"pQàÌo¯áá^6¥8Ÿ…ÐÌ@Ǽ|Á2IúHÅ*6KHKÚ$ùÒ+?¼ôÔ°ø#jÌ"DÀs›é1ž¹÷ž`Û›®¢e,NyG*G5•·k²;b`]à®ëŽ«Kä_Àd{z" ¢Wüë5åBå.@>J ]âL^YâËoÿ<ömä €ÒE_r,Y'û1’g žEÀÇF+“ÝvØ|YÖ!‹(&XŽYÝ6ööÒ~MÚ² u^{Š< ÛˆUΧƒ §ƒ±Á§ò B=Qø-Ø|ÍöÞòGPµÈ«äV%`6Ó3ÖBÇ’ì¨óš~@7ëë)[Û±†dVâSÀKxïÝ83=qÇ\f$-m7…ø, þ»o©{Jukp^Ÿß³•øÁd`ñ¡F ØÓ»oçÕ[v¼ãa" _æÃä;"B‘…˜xñõ{‰XvÛÙ¶©+°Ycÿ q ¡)€'ÀŒ©5ê¥W¼78ôâσɑC'ªQ¬râI<ö|pͲ¶ $ÜsÂGÿÝ=´y@õ÷íÕÅ< .’ÃJÕHT¹à´ˆáŒ$êqÆAŒ¹ç^)ò8·eo‡|ƒ?Å ˆ±|Š!ÔkîºÖ0Ëj»Ô‚Á ÜCvÞj‚¸ ©—Z6‰À@³ùT&…T¿á”ºíŒLz±èëXA4-É Òxâ=ŽíÂnÖö`Žˆ³ :Xª±” e4ëÕÿ"‹tAci˜ÏÁXd¨Ü[v#2ãÛ]BÙC¹ÂŸ—ŸšÉŒeb¨çžBwRW(ªé&èà™ø®Ÿg/àÛ¯/%¡Šˆ@3† ìßyÍæo„õÿEPã~DŒi-CƒÑlÆÈ©‡´Ç vfGvÖ«òD6€+±Â@iŸõ—]ôìm ÆŽ¾Xà xDÒÆ¥J=ü›„N؉hš27Îqö8ü±fGoœÄy ´zp¶2¸_¤(ƒôå,wPÆs áIršò(d“}ƒÓÝ·1§ _ùΕi¾¡²¶'Øé&›—®“l¼òÄ%K]§õ¼N¶9œ¦Dd››ó¶k±ä0²Ö›œâï³ýþ^€PLR'\‰—ÒŒï%¨þcÔ­©¢Ú3ôâ\"b}qžŒÀ |€åÀˆçÿGŒz“ÏÙÁ‰ ¼Ì¾À„~¼l@ˆ…©eâ"Ù_ B ØÕ,6é˱.¼äªûP´J8 …Mñs”šv2Iï- Þ ~àçaÒgã¡ qØö,Ž”½'›Ÿ x"€(õP!Òs–0z÷µèØ“ìÈuŸ· .H!ÆT‘r †2™rAXºº;@ðYâ"hÖG£Bhí³¥öÊ+ Ð2¨„©õÄðóDÛy%D/cL†x;åÉèXc.ŒÚ.ƒRålãŽjÅx¸ÒT wuæDn³…ÆoD|ŽåÖôØÀ×C¾d:|ß«òôžäº5)ÃÙ’Î5 q·8œÀø±ýO~`ãE¿ü½d{ç»$ž„“':8¼ƒbqD“}žüDàžd{×5°š2×:ÎÁ‚&¹§ÆûM6°fíVN`4#Õ ¬­ všÁ)æ±hï ¨$œ@;Û„_zù¯/¿ðx0;~(˜„ˆb+ÊI-'šÊïÔqJY îñŸª%ÅBbu+’ÂKÕe!æì¹»Ë¤û$º˜ã(E ¥ QÚœ×y)ñ½ûƒRøÖÌ–³¨êJÙH:–¼³«â‚O‹i"š.Ľœ‘Øgm9ÑŠÔ_DQ®¶MñN8ƒXü—þï&Qty‹ „{çш§éîÕ,¡·Œ£œöEw”tW(çÑN÷j“ÇižTÇÂШÃzêøÔs|SÁ:›qˆÏdÒ#×Og&îÉf†g`÷”—ü¦ì¨£Ê;÷ŸþZ:— €:hÐ%D —í;°ë£ë/¼ì;­Ýb9!ø˜ € Ùµ˜ÈÁ[ÑU+j,­%H/V9 7 ÿVØVµHƒpá[Þô³,@M(PðJ’½¦kyx<‚*A´ºr©ßˆ€C»›˜½³“Yv5B ·²Á&Ûœ%–ƒÈbá0”Í-Ù¸kE;Ú$Òù4³?êPâ%ä0k&( €Û®¼.KØ?ÌfúÉ÷¿ñÉïá9òí𦰠Z)Ê•QCá™’kŸŸåugi¤WR™Íçýê.†$Žû†ø¢p|8sÀiIŠr¥ŸÌLy|èèý…ì¤Öôˆ˜$ 1'3«„÷UDåˆêÉk•h—M4þx:ë=× €ÆN ¢å€ˆ>è?ðô¿XwÁew¶/[þÏAzAgÈ 0õÂö‰Ì¢ûŽ«Öž¯·m G Õ?JIÇ/¾¬ò˜†Pï%6ý),ÿ³Lð)ª/?º{Ê0£)Öö¶¾_hß !{EWÂwuðãR~œC|eàS/øáóÕñÇ*=Ê |yj¬çQdBª^´GK@IôùþV¾/™‹°>2¼^Iȯ䧫o`ãÈ ƒQ±|-Ä=ó¯Ön{ëmÝ+ÿ ^œ’)I„5±üéô(„…`­<™ÑæA¥xt»òa4†›G‚ Š/×D1›¶]ÒȽXÜìvø!ûfóÌ0—5°Xs#|'¿•é>Ë©2ýb)Žo‚,gùB´UÂfÇYÞ´µg-°4iv­dó“TÆL§ͨð0¡N{OñXÓÃCp<½Azd'‚=´'äO4#ÉWˆ-FÆ{†_Ñù½²Í‘ÞU´´ò"|G;nçúU¶ ßñˆ_~ø-³ìðÝd<C!›y²˜écCO<>âH2Qæ ¨Õ:>‰¬[ ÑZôèuK"ÔŠl$«Éx@žÎôF!Cƒcˆ@b¸ç¹_[sþÛrí]+]2€NÀ˜wøG$E!'0xìå`ã–‹A< ™e<§å°£í£bðÅæLDx깡EVoa{ëAc«OŠ YÁ{ð®žÜ«¤/õ3IÒ]$H†ºG³Ùå|<ñØ8cÒmq…íþõ„J#’Ž’˜çNÒlÔ„ŒA¢!äŸÎ Ù™ýØçïıê°fÖz1 wÌØÁ"ãÊf|kÔB[ü”K†KÑ>/¾KávOk|eO†L²XD¢Ç\7žË»Ÿ?mçö>ýÒvZf›;Ó) öè!Z)>¬®´kØë“K[Ñ)IþtªÓ‰hì:Œ¢Â“C=ÏþûuÛ~)G¹cºkQˆ}>Iª¥Ãžcì=¼'¢fÁ¦4kêy1O`-fz•(ñr 1ªD¹O:Ò„ ]ý²|õͰaojŸú;¢e Ã¥mÕ˜É[VỸ.ÂrÎi!´ù ߎ5¤„Þ’}4ƒÄ‰6<î°€ÍÎ úþ˜X‹{¡K°ù8ÆãÄݦ¯ n« n?¾pÆûák×Ñó‹³þtУ£^¨×_Ç +—þt@‡_ —Ìu¥ÃoaÆ¿55:xs63pÄdÝÝÍË–ÁÝ´›?D _S½Z\Ño ß7Ч ?¼4#$zžþ«·¼)ÛµrÃoÏXø:í ¹ ¹6)àér "°žhÂíÝÌ‚ì ȳVEÀ6‡žœÙÇ ÓvßA6Åç@f­'“¬Uú ÁÜ·Ö•aOËÅIYPù¼ü(r¾C;ä›°ò¼ °ôò d†g»«Ü=T ’ `å€L@9¬õž&~à.có[ßLt ¡mJQ\zmV·iñF,Žðaó$Ô+«æ#M.ŸZÛõSF|.á,;‹ysÇ%šŽ!„½yr¨÷Ó©¾!±Ïprð462y× èøo«@+´\ÅYpr:f~ßí7"ð}÷GBЖ’£½/þXïZµá“°pìa˃“ (–Ý f¬ëäNÜÚÉ\(5˜T…, Ù$Å4ªèÃaX¾ŸpÂËêCdÆ,C¦Gþê¼Õ×åüƒ%ê©“í냕k¶³r ™MÁ´°«}ÁÇarâX01¸iþ~ä‡)”¾°=V‚ ò®.‚ø¶bfN—JÔ%Tî…Ù>|xœí5"@¶…bÂw˜²DßnᇯÍËå‡/ÙK|ÿdj覩ÌÄ7s™QüðŘ¿Ä{ñÙœ¡°ßj8È6ü¬:4€Í.¶6líÛû{W¾ó¼5ŸbV,DXˆúŽì5Ÿ€6ˆ€_%ƒ,È¢Iá¶a¯Y«`¸³e眀fRà} ô­ª„Q–º7…4!tw¯Æg5aʦ™D±üëjg#“¶ ï°"ì 2Hóçf «.u¡ŒvÔ l"œ¼ yGy Z¿açÒD@ï©P%Îkб{>mDœ>>Yaާ§R£×ÏL§ïÍ –uø\óÃwe§¡.Sã—h‰‚4mÛ2tø¹Ogç¶Ï­Þ°í™%á‹%¨ ¨¤$à5pl°nÃv]–#]Çä×$3o[°`–Òk)¸¨ŸBX-ãBù$Ìn8PÿÕÚ»µ™‹é.»³AW÷6Ú.n¥‹t „ß ~–Û{h)j>´¡ÍíØlj3ªjPôWÝ0=«ŸjY½«¶ñçgüÊW8+?ÌdÌî¸yEu’>ÌUñ'S“#_J õ<ƒ&! е‚øZÆÕ1Þ©¬ qUÀ‹A¨I@•|ùs`Jqõú­ÿ£’€8à7€'ášÒl0²ÆŽJ-¨ÈÃJÚŒcAn%"9 § Áy$Eg=b;37—ê<’/zjùŠtª3C€Væ1uè\vQ°¬{ ÒþØû^Ât=˦‡h‹4¨ñb¬ïáTBí×´Áг¡‰Ö\q^‹ìácCté»êû^ÑÄðfY‡3¥øyKÄO´È|ð"ï\?6¸ÿïKRI8Ä÷8æÜ/ÔRѸÆÅqG A†(„t·aj`ßç%Ý[½ñ‚?Æ^¼ÂÈ^!·ÖùÃ=Àh)8oå`f±ÀdXB0)¸,÷ÕD |ÃáÇ¢heU/J¬NW‚eT5p+ yÞÙÝ͆? &FwB[f)BjN!¾žË}W² !lˆøa}Avt?!FîTz¤×íŠr|¾jÄ÷±ô%ѵ•y–­³î9|ÃÜTjW~NÚ  "ïx?|ë§jÒI¤ÓÊÚH': P9R$‰ŠmýmÿÿBŠ<·zÓ…_ŠxV¼!)ÿèð¨€ÍzˆÑpA:Û–ÌàE‚ Õ[Bzþ–V‚ëÞ «û¬ê&õ‰ÕhÃí«gEÓŒ0•8$˱ø!ÖVµ+l^ÍÚ~qEÊÍ(Ÿ„ÅP¸»Eeª>@•Ç*%†ñNóä\væŽÔHßM3Sc{ ^‘åi̬öÈoEÙË*À®?¯x vèTe"@0Ð/s=»zó…_c{ò0²Pô%Pèéň¦Àþð²3(’Èᥜ¢È%dðÍ^~ï¸'‘:êå…C‘ÝB[fÁYë)£jU².»Óª_?ÛG%U9üå’mÐ_•ñgu†±ôq¹EêÊ–Y£…ìÌ7FG{ož™ì;‚½± Å_:yåyAy˜–¬Ì·¨q<þ4@ý12ŒðœDàf1¿zÓEÎL%eÌцéÔ ÙŠÁª5˜“ЂQI8ùø«Î(1Ð¥î))ƒ¿ˆžÛÃðÇ?/ßóý±ü <ÜBÚÃAå#Uµa¡b÷8úk(½=×z_©¦]VX °Bùá‹ãp!µìÍe§ožº•C!âËŸ’æ%ÕW^%ß±šÜãÆï+X|älžh›éù nä×n¸èVì,¹žóæÃ`à81dw×BÁðÇÚd/qÛ}AâÊIë%NÏJ¨÷JñõŽðRHErþø~¦×±6éeÿž?ç¨Ór²kÿÐgŽèðrÅ—f2c7öÜNPB [¡KÅÒ¯¨¡\UãäU@ƒ,=„Èp³´fFzþ zníæ ô~ˆúC" KfXŮϤ‡ƒiXî85¶ƒòãBòq3,4¸r?΋ö˜£üÔÉî‘Þ_S™$:G^¬h€¯Àjðˆ_KŸq|ffrìúñáž{©}ÖÆÒ÷ãr.àø_QÝä—™ñÞ;P–åØžüf2m#,ñ¾¡•Žf0Ä­ötªoe,£Tù„’¢ç\Fo,pÆ•÷-Ûb?ÖÂhCTkvÁ{×ß_xÅßáRGU îd!†‚­P‡?ÿØÔä:üƒx>ôRÑoèð5h¯Sj€x›Æhj?z«ýDàÛLñÂ#MšúEÄ[W¡”åÑãW™– K–\ñ^ôbí×ûU­®,Òð”_¯@|åex°ÆFŸ1Wžû+>›hü3¾ ÀáýðÍ—{F4ô>ô@ƒœø€‡D „ŠÐˆÀwJ¥®ßrñ]€.Æÿ†?!(5¥‡hUž¶Éâ&Þè[Ñ y붉—@Âr1–ÇT÷wÓÐj·|Mz¶¬|¬(Å#¼¿Y.¢ñ½ñ޼òÐÓÇñ”ÎÝ;=3uCzøð“ J"*!ˆêðÃ"ˆïÇöu<6ÀÉ ¾á3¢µÌ¦úÿÈú­¿ " ©ŸÓÿ¹ÙÌxU ! FŸšñGǪìvb_ ëåYêÞ¡ˆò!:/×êÛcuTWT¾•èK‡O\‚h†~ø…bö¶éôøWÓC÷h` ,–>ÕH•'>%ߘêJìaãçµ8ùñ1X¡u›ZfÓý÷õ?´aë¥÷aÿ/E»óZŠD—•Sú"­YÀN/¼¡É[ï¶Ë둽ñË٫ʳËòÏâ Œ…~ø%…Ô«?R(doî»e&=p؉0ìqÉtøa Ä/õ™sÒ ¯ì[—@ ù4µd3Cöõ”>¸é‚7Ûûå`‡Ö»J4æ±·²¦¥¿Œ‹å“ÊwíÊãSGºUÆÝÚçžT<©¨*|9"Ø#oK_¾EŠ„Ô|ŒÍ@¾ŽÕÞ_f3ƒòÃWƒ´þ×Ë€#,ß7´¢†ðYãð:@ƒ¼ò €¸KYd-¹é‘ŸÙ÷Äû¶ì¸âfFö¹ÂGØ© ]>¡Á«M”¡ߎ‹>¹ÂjÞð”2*ÐÒ.àÝhY‹¹ÃîŸ%r³m–"•Š/ó¹¯b¼s{&u,3 |a,}ÛéÃ7L÷U`E þaãxfŒ@ƒ¼ºï àFõg^„I¶ {ªïÐî÷oÜö–û‰²³[GB:¸³“¯¹ŒJå“Ú2ª–5ˆÅÇr1vA|qü˜à "€÷0l¾zðôLzì+èðï.ågg¨†^h Û2«ž*¯\zm#wΔˆÊ„Δ6mí  Ì:073þÌ‘ý;¯Ís}lwUø`xf¡—G TOu^}­ûºéŸ…Ç*ܶ\>«Ë-("àó9£„DzªDþ\ùöTÄŒˆHÙ,(ò½Ø£¨ò>Òwð™+G޽øMÔyrÁÅߤ ¾­eô®þ\I¾¾ÆñŒ85ŸH€/³aôà±$Û“?tÿ×@Žàz0[ ®ÆjT©síÐüŽê¹á—{½â×£_äfÝ™ß ±ŠøQ¡ ­Íäjº¿ ×ðÊKγο*5ü¾¾»¯9úâ÷œ_v€˜‰£¯¹ºõ‘–4NÏäh€S÷u„ „qÍ}«ñ:ˆs0Áµ>¶çÊwÜä>šm‰× ýœQÞX@~{Àò»Búâ[ÍøzMˆŸËçsßê}WÿÁ?8ÖÿÒ ¹ }2ħXYíYË„øJ¾wÕø=ëF !8µŸL¡é4ÏL)"ÐxÛ“?œL4_\,•r̬D¬5¶âëñÈ7"DT;„çþÑbǪ¼•Èï_ ɉîéf…>½ ý;GGß8›xÑqõA…?,A V'Ø8UÕHgò4À©ÿ:BOÞc/ï|ÏæíW<ȹoÁv& Ðî4 £e©"øäOý1J#ü=ksÅEÕ¬ï;ÎÖŽÍç&{;Ö]a³ Éåf¿‘¼e.Õw$ ¡]ᇖÔ@|?¤çرANÏvŠà ¹þ£ûžxÿÚóßrGçÊ_Ìl”Í4Zæ;$ gXó$Ñ6Ò2+Žë¥Þã¨2Y¾p}oÅDüðmC“£Å|þÏ&Fÿr&Ý7ä‚sà‡høáGFþ\?m€Ó÷… kE¨"Î:{p çÙkWn¸øÎî•ë¯)b7Kt}-B̵D#lPùÄ]WDªÛf½`ÿUÉ©ò@nv=.5+` òˆýXíÝ4Ü»ï›ù¹tâD&âðÄWt]÷ª[›¨%¾5þ~ãxŽ@ƒœÞ*B:O¸ùù‰±c{ߟÏf¿¼jÝÖOÙ¯N@‚X¾ƒdlúxA ¾ÏÁ§*ʰ€ôØqÈŒ>oh÷ù*>;º»˜Ÿ•wŽ2ò<†ôO!· ñCÂaHß@|?Üçøñ5#šl”üñ×h÷üžT²yvªIb쓜>½fÃ…_cS‘N¬û'+€ÁI/U(cdybÖbžÔC%·šÇñ«Ç-O†;Í,îÛbñÊüÒcsÓé@ü Ù)ÖÿórÇ•_êJIôõ=”¬+ó,ÿ û¤þ¼aí¤¿ÞkFôA”tôç'ÝÚ³ôúë9õ]~ð-“#‡ÿz*5´gå†_M¶v\ÎF›mŒÝËî"Dí½QãŽI°™¤'Š®›² ý95¤‰™Ñ›XB”äss“™þNavü9-é©Oy¼~M,}×¼³tpë4Û÷ÇëdiÜ Gà´€æf·QÆòåËm×\m­ýý0žhbÇ ÖLìû§Š3ƒWµ/»ðÝ-]Ýÿuú¯2‹wã9ë¦f2šÚÞÛqÀ8€„3,´/È”—~ªT(ü ;7ý­ñ¡£?œNÏň]šd›_bÿëgK¥BÑ/2ÆBm1jc¥œC?ÒdÄ1]Îçól…Ö}õìôtå´}¥T*ÅöÔ‚hár§§Kgt©éD40Íã#ƒ%þe¦ço~KÐÔúÁ ¾â"ø¾ Þ"® ´O)“?‹Qá@PÈ€¿í JsGƒùÌî?Î_?~ÕÀ –¿Í²o™KzvÎ"~ØG;„Kš V¼ÑFZjNH$\ÑW]uUpÝu×±}Öò ›ÕVÔRCy*÷@,ÿ?*þ>)ÿ>)ÿ;'ü>&÷?)þ=)ÿ7&ö' -‘’’ÿÿÿøøøÿÿÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿÿÿùùùÿÿÿ¨¨¨7'ÓP9,ÿL6'øD0#ÿE2%ýD/$þD/$þA."þD/"ÿ9+!ÿ5*þA-"÷D."ýB/"ÿ>,!û-² :ÿÿÿñññûûû÷÷÷øøøøøøøøø÷÷÷úúúÿÿÿÿÿÿÿÿÿÿÿÿþþþÿÿÿùùùÿÿÿ¨¨¨9'ÖM7*ÿqQBøQ9,ÿA."ýF2#ÿA."ÿB."ÿ@,!ÿD-!ÿE5þk:,ÿ:)ÿ2(þ=+÷D-!üD."ÿ@."þ)¾"G“””ÿÿÿøøøÿÿÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿÿÿùùùÿÿÿ¨¨¨:)ÚO8,ÿtTCùkM=ÿD0#þF1#ÿD0#ÿB/"ÿ@,!ÿE."ÿ9(ÿ•B.ÿÓeMÿ¯WBþ|>0ÿF*!ÿ1'ÿ7*÷B-!ûA,ÿF3'ÿ!ÊlfcT¬««˜™š¡¡¡ ŸŸ   žžž£¢¢™˜˜¿¿¿ÿÿÿýýýÿÿÿÿÿÿþþþÿÿÿùùùÿÿÿ¨¨¨";)ÞQ9,ÿrQAúeH8ÿ þ:)ÿH3%ÿD/"ÿD/"ÿ@."ÿD-"ÿ;.ÿ™%ÿê70ÿÜS@ÿßlSÿ¾^Hþ‹C3ÿV1&ÿ5(ÿ4)ø?*úG1#ÿ5 ÿØd aaaÿÿÿùùùÿÿÿþþþþþþÿÿÿùùùÿÿÿ¨¨¨'=+ãT<.ÿvTCúcH:ÿþÿK9.ÿD-ÿF2%ÿB."ÿA."ÿA-"ÿE-ÿÇ)ÿÿ3*ÿù2(ÿñ6-ÿßE8ÿÓWBÿÃYBþšH7ÿe4(ÿ=*ÿ0'ù@0%ùO:.ÿM9-ÿB3)á,!tijjÿÿÿùùùÿÿÿþþþþþþÿÿÿùùùÿÿÿ¨¨¨  *>+çT;-ÿxTCúX>0ÿþÿ]_dÿN?5ÿD,ÿF3&ÿA.!ÿB."ÿ?,"ÿO/!ÿÛ, ÿÿ4"ÿþ2ÿÿJNÿÿW\ÿò/ÿâB;ÿÔG5ÿÄR>þ¥L9þs9-ÿD)ÿ,!ú4&ø=)þ?)ÿ6$è ‚ cccÿÿÿùùùÿÿÿþþþþþþÿÿÿøøøÿÿÿ¦§§1@.!ëW=0þuO>ûXA5ÿþ,,-ÿzz…ÿrp†ÿB.$ÿH3(ÿE/$ÿA/"ÿE1#ÿ:)ÿ[<-ÿ÷D3ÿÿTZÿÿrwÿþ’˜ÿÿjpÿÿ2ÿÿ4)ÿù1$ÿç3-ÿØB4ÿËO;þ¯O<þ‚B2ÿQ/$ÿ9*û9+÷@/"þ@+ÿE4+î=cccÿÿÿùùùÿÿÿþþþþþþÿÿÿùùùÿÿÿ©¨¨5;)îX<-ýyR@û6ÿþW]\ÿ•™ªÿŒ‰¦ÿom„ÿO91ÿD/!ÿG2%ÿ@.!ÿF2%ÿ7$ÿeD3ÿÿ^Jÿþ„‰ÿÿ¢¥ÿÿ]`ÿÿx{ÿþ>;ÿþ75ÿÿ3$ÿÿ3'ÿý2#ÿï0'ÿÝ90ÿÌJ9þ²O;þƒ@0ÿV0$ÿA,!þ4#ôG7*ÿ$ßigf ÿÿÿùùùÿÿÿþþþþþþÿÿÿóóóÿÿÿ ¡¢3I8,ðV7&ýxM9ü5"ÿ þLPRÿ‰”ÿ†ˆ£ÿ¯ÿ‡ƒ˜ÿF."ÿH3&ÿF1#ÿA/"ÿF-!ÿ71&ÿf*ÿÿ”ÿþsxÿÿ:6ÿÿPUÿÿ†ÿÿw€ÿÿedÿÿY\ÿþciÿÿ3ÿÿ40ÿÿ3#ÿ÷1$ÿå6.ÿË@2þ C0þk;)þM.ÿH4)ö+ÿpkiMÿÿÿõõõÿÿÿþþþþþþÿÿÿùùùÿÿÿ­¬¬"ÕlH7ÿuD.ù9'!ÿþ€y„ÿ‡—ÿnt‰ÿŽ©ÿŒŽ©ÿvn†ÿF.!ÿH3(ÿF1#ÿA0#ÿF-!ÿ70%ÿe+ÿûseÿýCGÿÿ3ÿÿ3&ÿÿ:8ÿþª¬ÿÿ{ÿÿ¯°ÿÿ˜ÿÿ2 ÿþ84ÿÿ3$ÿÿ4.ÿÿ3$ÿý3)ÿë/$ÿ¼3)ým5"ÿ\;-÷-ÿztpvÿÿÿúùù ÿÿÿþþþþþþÿÿÿ¹¹¹±²³zxHd6ÿƒL3÷9+$ÿþ\a`ÿ€}’ÿŠŠ£ÿ—™±ÿzz•ÿtuÿ{t…ÿB*ÿK6*ÿF1"ÿA0"ÿG/$ÿ6,ÿi4)ÿþ7#ÿý2ÿû41ÿû1ÿÿDFÿÿ_bÿÿntÿÿ’—ÿÿQRÿÿ€ƒÿÿpvÿÿVTÿÿ;3ÿþ2$ÿÿ;3ÿü<;ÿÿ3$þ|*!ÿ?4&ö> ÿ\WR‘°°°ºº¹ÿÿÿþþþýýýþÿÿ>==%wyB'ÿ/ ö!))ÿzt|þ}€”ÿ~šÿ›™·ÿŠŠ¥ÿnn‰ÿˆŠšÿ_YYÿG1"ÿI4)ÿG1#ÿB0#ÿD/"ÿ?."ÿP/"ÿº2*ÿ÷1*ÿÿ3&ÿÿ3&ÿÿ4.ÿû3+ÿú;6ÿþnqÿÿIGÿÿŠ‹ÿÿmpÿþ©«ÿÿw{ÿÿcgÿÿ{€ÿùijÿÿ2þ’73ÿ:+øL."ÿ)!«===ÿÿÿýýýýýýÿÿÿIHH>,%”`5!ÿ?85ö]adÿ€›þ’±ÿ¯ÿ­ÿˆ‚˜ÿleqÿ"&&ÿ ÿN9,ÿF2%ÿH3&ÿD0"ÿB/"ÿB.$ÿ?/$ÿ6,!ÿR)ÿˆ% ÿÃ'!ÿó1'ÿÿ3 ÿÿ65ÿÿDFÿûLRÿú[_ÿþVZÿÿ˜šÿÿ„†ÿþ¥©ÿÿ¼Àÿú†Œÿÿ\eþ¡^Zÿ;ùL2&ÿ4'ÂHHGÿÿÿýýýýýýÿÿÿ@@?=)¯d=-ÿto{ø‚ˆŸÿ““¯þ‘’±ÿwwÿ’¢ÿtn{ÿÿÿ(ÿL4%ÿG2%ÿH4'ÿD2%ÿB/"ÿB-!ÿA,ÿ?*ÿ?/$ÿ95'ÿ75)ÿP+!ÿ…&#ÿÀ'!ÿñ0 ÿÿ3 ÿÿ2ÿÿ4/ÿû2ÿúiiÿýÿÿ†ÿúZ^ÿÿ•œþ¶Šÿ@ ûN4)ÿ4&×???ÿÿÿýýýýýýÿÿÿBAAI3(ÆiD=ÿ†ƒžúœœºÿ„ƒ¤þ”•±ÿˆ‘ÿ‚w†ÿ73=ÿÿ-/1ÿ9/)ÿL>3ÿIF=ÿHF@ÿDB=ÿD?:ÿ@;3ÿI81ÿ\91ÿ?*ÿ2 ÿ9ÿ>/"ÿ71#ÿ42#ÿO/$ÿƒ*&ÿ¾(%ÿï0(ÿÿ3#ÿÿKOÿÿECÿÿ\^ÿõbcÿÿgjÿ¾/&ÿ@7*ýO+ÿ;.$è@@@ÿÿÿýýýýýýÿÿÿ@??Q8,ÜoOMÿœ›ºü‘®ÿ¨þ¡ž±ÿcepÿI@Nÿÿ0-/ÿÿ'%"ÿOQLÿGICÿIJDÿDE@ÿDC?ÿ?B=ÿTGCÿ¶c_ÿËprÿ¶xzÿzbbÿK.%ÿ5&ÿ=&ÿ:+ÿ1.!ÿ20"ÿN.$ÿ~'"ÿ·#ÿê0*ÿÿ3)ÿûU]ÿÿIPÿÇ&ÿ@9*ÿM*ÿ>0%ø =??ÿÿÿýýýýýýÿÿÿ???Z?1ísWYÿ‹‹«þ¥¤¹ÿrszÿ94;ÿÿEAFÿKJIÿkoqÿÿ++&ÿPPIÿHGBÿKJCÿDD?ÿDC=ÿ@A<ÿMC=ÿ€)(ÿµ>?ÿòosÿÿÃÏÿóÑáÿÆffÿsA;ÿP2&ÿE)ÿ=&ÿ2'ÿ0-ÿ6:,ÿM1(ÿ~$ ÿ²$ÿó0ÿÖ,(ÿ@/!ÿL,!üB3&ÿ4<==ÿÿÿýýýýýýÿÿÿ>=='`@2ü„otþ¡£»ÿb^eÿÿÿOOOÿÿ653ÿ‰Œÿ!$ÿ++&ÿRRNÿHHBÿKJDÿDE@ÿEC?ÿ?A<ÿUEBÿ§!ÿ½"ÿÀ$ ÿÿ3 ÿ»çËÿ椗ÿæ=5ÿVSÿŠLDÿvKCÿh?6ÿI0&ÿ4ÿ/ÿ/0"ÿ/4&ÿK2(ÿi'!þD-!ÿA'ùG3'ÿJ<==ÿÿÿýýýýýýÿÿÿ>=>ÿÿÿýýýýýýÿÿÿ<=?TmOCÿ+øÿXUVþrxyÿÿfijÿBILÿJLMÿAGIÿ6=AÿBEAÿOOIÿLLGÿKJDÿEF@ÿFC?ÿ;@;ÿ`IFÿ”"$ÿ¿#ÿÉ'!ÿÿ2ÿ•à°ÿµ¾ˆÿÿ2ÿÌ) ÿÃ$ÿ… ÿ™ÿº/.ÿøsvÿüÛÜÿቇÿPFÿcOCÿP7,þ@+ÿ6 öH3&ÿ {;<>ÿÿÿ ýýýýýýÿÿÿ :;;n`B5ÿ=1-÷MRUÿ-,,þt|~ÿ035ÿ`cdÿÿÿÿýýýýýýÿÿÿ9:;"ˆW<.ÿF?:öy‚…ÿ=<;þ^hlÿIPUÿZ]_ÿMNNÿehiÿ>>ÿÿÿýýýýýýÿÿÿ;::230»XYTÿ577øWagÿkpsþilmÿ^ceÿGMOÿcfhÿHORÿÿÿDC=ÿUTMÿPPIÿMMGÿEFAÿGE@ÿ;@;ÿbJHÿ¨)*ÿ¾#ÿÁ**ÿÿ2ÿžÌŸÿ¡Î—ÿÿ2ÿÈ' ÿØ)ÿšÿ©ÿÊ)ÿã,ÿáuRÿ“Ò¬ÿÿ2ÿÀ'ÿ¬ þy)+ÿIMDûOLFÿ%&"×@??ÿÿÿýýýýýýÿÿÿ>==:;7ÑZYTÿZ^^ú|ƒ†ÿipsþZ\\ÿU\_ÿ157ÿhhhÿAGIÿÿÿGGAÿUTNÿQPJÿNMGÿFGAÿGE@ÿ;A;ÿdGFÿ ÿÅ$ÿÍ)ÿÿ2ÿœÍÿ—Ù™ÿÿ2ÿÞ.)ÿÒ(ÿ¡ÿ° ÿ¿%ÿâ,ÿèiCÿ‰æ¶ÿÿ2ÿÍ(ÿ° ÿ{!#ÿIJCýQKFÿ),&éBBA ÿÿÿýýýýýýÿÿÿDBB >?:årwtÿnsuü[beÿEORÿNNNÿbhkÿÿ{ÿ9@BÿÿÿKJDÿVVOÿQRLÿNNHÿFGAÿHFAÿ:=9ÿgUQÿ²ÿµ]]ÿÁQVÿÿ3ÿ Ì¡ÿ›Ñ¢ÿÿ2ÿØ+ÿÒ'ÿ«ÿ­ÿÈ*ÿä,ÿê^:ÿŽì¶ÿý4 ÿÒ(ÿº"ÿzÿPICÿOLGÿ00+øFFFÿÿÿýýýýýýþþþIIHDE@õqywÿR\`þZ^`ÿ=HLÿGFGÿhptÿÿxy{ÿ!"ÿÿ+++ÿPPJÿVVPÿTTNÿOOIÿGGAÿGGAÿ=?:ÿ]VPÿ‚‰†ÿ„‘ÿ‘žžÿ©–•ÿ˜¤ ÿ¢¨˜ÿÜf`ÿÔWWÿÇ96ÿ£" ÿ ÿ¼$ÿß+ÿ÷P!ÿ‚ì¹ÿù8ÿÉ&ÿ§ ÿy"ÿPHBÿOMGü22-ÿIIH1þþþþþþþþþþþþMML/GJDÿageü"&(ÿgmoÿ:CGÿ]\\ÿLPRÿ999ÿYYZÿÿ'&%ÿ(&$ÿUUOÿXXRÿVVOÿPPJÿHHCÿGFAÿAB=ÿOIEÿYRPÿWSTÿXSTÿkqqÿ{yyÿƒÿ‡œÿŠ—–ÿžŸžÿ¢•”ÿ©‡ŠÿÀzxÿÐ[_ÿï_EÿŠíÁÿûCÿÔ(ÿ£ÿyþNE@ÿQNIù550ÿMMLHýýþþþþþþþýýýQQOFLNHÿWYWùÿrtuþ+-0ÿhggÿ%%%ÿIIHÿQQOÿ00,ÿFFAÿQQMÿ[[Tÿ]^Wÿ\\VÿVWPÿKJDÿGFAÿBC?ÿKGCÿWQNÿWONÿd][ÿMEEÿŽŒÿ›ÿMBAÿ]XYÿ[YZÿbggÿluuÿu††ÿ€‘ÿšžžÿ—’ÿ¹’ÿÂÿ¹jjÿšMPþUHCÿPOI÷972ÿQRP_ýýýþþþÿÿÿýýýXXV^NOIÿFFC÷(()ÿZZZþ! ÿ``^ÿ662ÿBC?ÿNOIÿWXQÿ[[Tÿ]]Wÿgg_ÿmmfÿjjbÿba[ÿPPIÿIICÿGGAÿEE?ÿDB=ÿGB=ÿPJEÿNHEÿsmjÿypkÿOIFÿYURÿTOMÿVOOÿUNMÿXMMÿWNOÿ_\[ÿŠŠ‹ÿxƒ„ÿl||ÿ~ŒŒÿŒ’‘þWUPÿOMG÷;:5ÿWWUxýýýþþþ ÿÿÿ ýýý`a]yLMFÿ975÷""ÿFFBþ==7ÿKJDÿVWPÿ[[Tÿ]]Wÿ__Xÿcd\ÿiiaÿzzqÿ…ÿŽ…ÿ‰‰ÿXXQÿMNHÿLLFÿKJDÿIIEÿGHBÿDE@ÿDD?ÿ9:5ÿ9:3ÿFC?ÿDA;ÿFE?ÿHF@ÿMIEÿRMHÿ_WTÿUPNÿš”‘ÿj_\ÿNDBÿUMMÿTOPþFEAÿOMGö=;6ÿZZX‘ýýýþþþÿÿÿüüüeeb’GG@ÿEE=÷EE=ÿPPIþYYQÿ]]Wÿ]]WÿaaZÿff^ÿmmeÿvwnÿ‚‚yÿ´´§ÿííàÿØØÊÿÔÔÆÿttlÿjjaÿ[[SÿPQIÿMMFÿKJCÿKICÿHHBÿMLGÿLLFÿFFAÿFFAÿDE@ÿBC?ÿAB<ÿ@A;ÿ@?:ÿEE?ÿD@;ÿHGAÿKLFÿKLGÿLLHþ@?:ÿGE@ø;;5ÿ\\Zªýýýþþþÿÿÿüüýjkg¬IIAÿXXQøYYRÿ]]Wþ`_Yÿdd\ÿlleÿuumÿ€€wÿƒÿ˜˜ÿŸŸ•ÿ½½°ÿ××Ëÿ®®¢ÿššÿxÿªªŸÿ£¤›ÿ„„~ÿvvpÿkldÿdd\ÿ]]VÿVVOÿPPJÿOOHÿLJFÿIICÿHHBÿGGAÿFFAÿFF@ÿBC=ÿDE@ÿAA=ÿ@@;ÿ@@;þA@;ý@@;ÿFFAø992ÿbb^¾ýýýþþþþþþþþþrsn¿TSMÿa`Zù``Yÿjjbýsskþ||tþ††|þŒŒ‚þŽ…þŒŒ‚þƒƒzþxxpþmmeþa`YþVVPþOOIþTTOÿa`Yÿjk^ÿkl\ÿll`ÿnnfÿoohÿoogÿmmfÿkjbÿgf_ÿbc\þ]^WþYYSþUUOþQRLþOOIþMMGþKJDÿIICÿHHBÿGGBÿGGBþDC=ÿHHCú::5ÿddaÁþþþþþþþþþýýýxxt¾_^Wÿrrj÷vvmÿ}}uþ€€wÿ~~uÿxxpÿoohÿff^ÿ\]VÿVVOÿQQLÿOPJÿPPLÿOOLÿPPLÿNNMþJKVþHHpÿIHzÿHHaÿIILÿKJDþLLFþNOHÿQQLÿUTNÿWWPÿXYRÿYZSÿZ[TÿXXQÿUTNÿTSMÿQQLÿNOHÿLLFýIICûGGAøBB<ûEE?ï992ÿ\\Zœýýýÿÿÿÿÿÿúúûnnjˆnofÿrrj÷jjaÿee]ø\\VøUUO÷PPIöMMGöLLG÷LLG÷LMH÷MMHøMMIùLLIúMMLûJKMüIJUÿLKlÿNM›ÿSQÝÿSQáÿNN¦ÿMMqÿMLYÿLLMÿIJIÿHHFüGGEûFGBùFGB÷FFA÷LMGöRSN÷RSNùUUOüVVPÿWXQÿYYRÿZ[TÿXXRþ_^YÿDC?ìRRR(þþþþþþýýýÿÿÿEEC650¡ZZSóOOIþRSNÿRRMÿTTNÿTTPÿUUPÿUUQÿTTQÿRRPÿRRPÿQQQÿQQTÿOOQÿZZ]ÿppvú{{‡ø||•þyx¤ÿpo¢ÿihÿ^^uúQP[÷JKPýLLQÿNNPÿOOOÿPPNÿPPNÿTTPÿEEAÿ561ÿ772ÿ430ÿ11,ù--(é(("Ú$$ƨ q;;;ÿÿÿýýýýýýÿÿÿ@@@ 4CR!!]%%"k++({--+‡11.•763¤::7±=<;¼AA@ËBBAÕVVTß}}}ü}}ÿttzþno€þii~þeejþ``bÿTTXÿHGGþGGFôDDDäAA?Ó995½::9¨! kkjx¦¦¦_•••I›šš4”””!ŒŒŠŠŠ‹Š‹†††¤¤¤ÿÿÿþþþýýýÿÿÿ@@@ %NFE\ÂPPðUU×ÿTSæÿOMŸ÷EDg×+*;x ( “““ÿÿÿøøøÿÿÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿýýýÿÿÿ@@@   ECaxPOp±    ÿÿÿòòòýýýùùøùùùùùùøøøøøøøøøøøøúúúÿÿÿÿÿÿýýýÿÿÿ???56:QBDM‹’’’ÿÿÿøøøÿÿÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿÿÿEEE  D<ŠiTJµ¢ ”””ÿÿÿ÷÷÷ÿÿÿýýýþþþþþþþþþþþþþþþþþþþþþÿÿÿÿÿÿýýýÿÿÿ===Z?æÔkJÿÿ @"ÿÿÿøøøÿÿÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿÿÿnnn;;;IIIBBBFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDEBGFIBB<vhÅs†tëšIGPEEEEEEEECEEEEEEFFFAAAMMM000ªªªÿÿÿùùùÿÿÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ( @ ÿÿÿÿÿÿùùù[[[999AAA>>>@@@???@@@<=>FEB"bVP¤cVP»[RMƒGEC":<>=>?A@@@??@@@;;;OOOóóóÿÿÿýýýÿÿÿþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷%%%+0 ä:#ÿ5 ý6 ÿ2ó œ+ñññÿÿÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøø000 0D2&êK8*ÿF3'öF3'úE2&ö>/%ýB2&ÿI6)ù6'« 7ííìüüü÷øøúúúùùùùùùþþþÿÿÿÿÿÿÿÿÿÿÿÿøøø(((5A,ícF7ý@,ûA.!ÿA,þ:'ÿd7'þU1%÷-ü9'ÿ>*ü4"¹Cóóóÿÿÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøø+++ :D0"ñtUDüQ8,üD/"ÿB2%ýF/$þ5,!þ7(ÿÔ[FÿYDÿd:-÷9'û9+!ÿ@,ÿ?.$Æ|wtQuuvoqrtttvvvuuuéééÿÿÿýýýÿÿÿÿÿÿ÷÷÷))) @F2#ôwWFü1üÿM6)þ>.!ÿI.$ÿ2.!ÿ—$ÿÿ3&þñ@5ÿÜSBÿªP<ÿp<,øA,!ú5'ÿ(ÿ! Ò_ ÚÚÚÿÿÿüüüÿÿÿÿÿÿùùù+++ FI2%÷xWGû+ ý!ÿ`Z_þB-ÿB3&ÿH-!ÿ2-!ÿ¾2)ÿÿ3&ÿúY\ÿþJIþô2$ÿßK?ÿ¶G5ÿA0ùR9-ùB3)ÿH4+ÿB0&Þ)h ßßßÿÿÿüüüÿÿÿýýýòòó)((JU=0ùgC1û,ýOVYÿ§þiaqÿB,ÿD4)ÿG,ÿ50#ÿÒSAÿÿ…†ÿû‘ÿÿabÿÿ3"ÿþ2þø3*ÿç?6ÿÀC3ÿ…>,ùI.û6$ù>+ÿ!ÕÕÖÿÿÿüüüÿÿÿÿÿÿùùù652DN2!ûkE3ú%þPW[ÿ„ˆ˜þ‘“´ÿlbpÿD,ÿE2&ÿF3&ÿ6 ÿÒtdÿÿbfÿû4+ÿÿ‚ÿÿy{ÿÿzÿÿ<8ÿÿ2þý4+þð82þÂ=2ÿp8(÷O4&ÿ9'ÙÞÞßÿÿÿüüüÿÿÿãâ⺽¾gJ<Ãp@,ÿ!øQTYÿ˜˜¬þ|€™ÿ…‡¤ÿlakÿA'ÿF5*ÿF-ÿ7/"ÿÖ:0ÿÿ4)ÿû1ÿýPOÿû†ÿþŒÿÿ`bÿÿ^]ÿÿ94ÿü2 þÿ5,ÿÔ+"þM4%ÿA*𪪩æææÿÿÿÿÿÿj@.ê,ÿbfqþŽ‹¤ÿ‹Œ¬ÿŒ‰¢ÿjk{ÿK@;ÿH2#ÿE3&ÿE.!ÿ;0#ÿ„,&ÿ×-+ÿ÷2+ÿÿ3"ÿÿ:3ÿÿ\Zÿûkkÿû“”ÿÿ‹ÿý‚†ÿÿ…ÿé30ÿG."ýL4)ü,~ÿÿÿÿÿÿƒƒ„(aA3ùgdlþŽ«ÿ……¥ÿŽ¥ÿngsÿÿ,ÿM6'ÿE1%ÿB0#ÿD-!ÿ()ÿ$$ÿZ(ÿš,,ÿÑ-+ÿ÷51ÿÿ71ÿÿ:6ÿÿbgÿú¤§ÿþ}‚þõ‰Šÿ\B6úL1%ÿ?‚ƒƒÿÿÿÿÿÿ~€;qQJÿŽŠ§ú””´ÿ•—¬þuq~ÿÿ ÿB;5ÿKE<ÿFF@ÿEA;ÿ?<6ÿvG@ÿxJEÿ=1&ÿÿ$)ÿU"ÿ'$ÿÍ)"ÿø96ÿÿ>;ÿÿ]^þûXYÿa$øM>1ÿX}ÿÿÿÿÿÿ}TvZYÿ““°øŒ‹šÿMJSþ-*0ÿTUUÿ),-ÿ+-)ÿRSOÿDD?ÿFD@ÿ=D?ÿ€DCÿÑUVÿ㞦ÿ—ÿy:2ÿ@9,ÿ+,ÿ'ÿH ÿŽ%"ÿÇ23þõ4-ÿl#÷I>0ÿn}€€ÿÿÿÿÿÿ~‚l‹tsÿ‰ˆœ÷ ÿ253þ?@@ÿPRTÿ?FHÿ+)$ÿUUOÿDF@ÿGB=ÿÿIPRÿAB=ÿPOJÿGICÿFA=ÿ?GBÿ–0/ÿÇ&ÿó0ÿœÐÿçJ&ÿÒ(ÿ˜('ÿ®46ÿë}~ÿÝ££ÿŽJDþMG9ÿ:+øL6,ÿ  €ÿÿÿÿÿÿ}€ ZJCÿBHHø[_aÿFLNþGJMÿX[\ÿRVWÿMNJÿONHÿHJDÿEA<ÿ@HCÿ›+)ÿÊ& ÿó0 ÿ¨ÓœÿèN3ÿÝ* ÿ¢ÿŸÿà+ÿȯŽÿÓ]Mþž2/ÿg\Qø`VMÿ¸€€ÿÿÿÿÿÿ~€$ ¹VRPÿOX\øIORÿbgkþ]abÿCHKÿÿDC?ÿRRLÿHJDÿFB=ÿ@GBÿ¡10ÿÌ'ÿû2 ÿÑ–ÿêN0ÿß+ÿ¤ÿžÿâ,ÿ¶¥yÿÚZ@þÏ'ÿk&'úXYRÿÍÿÿÿÿÿÿ€-.)ÏPSRÿ\ejúpsuÿMQRþY]`ÿ37:ÿÿMNHÿRQJÿKLGÿEB=ÿAHEÿ“)"ÿº"ÿ÷1ÿ¡Ñ•ÿà\8ÿä,ÿ®"ÿ¦!ÿæ,ÿÀžuÿØfNÿÕ(ÿy üWQMÿ!ß‚‚ ÿÿÿÿÿÿƒƒƒ 793â{ÿZaeýW[]ÿBHIÿ[^`ÿ269ÿÿQQLÿRRLÿLLFÿFE@ÿAD@ÿ[Zÿ¾OQÿø7ÿ¡ÖŸÿéY2ÿë.ÿ® ÿª!ÿæ,ÿÁ™mÿÌ{XÿÝ*ÿ&&þ[RMÿ%%ò„……ÿÿÿÿÿÿ‡†…BGCõelnÿ?FIÿOTVÿMPRÿTUVÿ)+,ÿÿTTNÿTSMÿMLGÿDE@ÿDA;ÿrspÿw‡ˆÿ“ˆˆÿ˜“ÿ·„ÿÆjiÿ±NNÿ¨EEÿê.ÿË‘mÿÌ…aÿØ)ÿrÿ[LHþ++%üˆˆˆ,ÿÿÿÿÿÿŒŒ‹1@B=ÿ???ýORUÿRTUÿ???ÿHGGÿ652ÿFFAÿ]]Vÿ_^YÿRRMÿHICÿGFAÿNFCÿWKIÿRQQÿŽŠŠÿnyzÿatsÿy€ÿ€‡‡ÿÿŸšÿµƒÿÌXXþ’@Dÿ_QMú-.(ÿ‹‹Š>ÿÿÿÿÿÿ’’‘F9:3ÿ===ù>=;ÿGGEþIIEÿPQLÿZ[Tÿ_^Xÿfe]ÿlmdÿ_^XÿFF@ÿEE=ÿFG@ÿMMGÿMHCÿhe_ÿYMGÿM@<ÿMDBÿRKKÿNQPÿrrtÿ‚ˆŠÿfxwþ{}}ÿ[YTø0.)ÿXÿÿÿÿÿÿ——•`770ÿDC?÷IICÿTTNý[\Vþ`_Yþkkdþxxoþ··«þØÙËþ¦¦›ÿdd^ÿ\\ZÿKJIÿFFBÿBD?ÿ:;5ÿ?B=ÿBGAþDE@þHGAþKC=þVRMþ\TNþI=:ýDA?ÿMLG÷42,ÿ‘’mÿÿÿÿÿÿšš˜vAA:ÿbc[÷_^Xÿmmfþxxoÿ†‡}ÿ”“Šÿ––Œÿ²²§ÿ«« ÿ~~uÿŽƒþ‘‘þuucÿppfþgf^þdd\ÿ[YSÿTRMÿNNHÿIICÿBD@ÿ=?:ÿ:<9ÿAF@þ==7ÿFFAö'&!ÿ““‘…ÿÿÿÿÿÿ¤¤¡†WWOÿttlíttlú||tö{{søttløhh_øZZSùKJCûBA<üEEAýGGLÿLKnÿQQrÿUVXÿYYSÿYZRÿ[[Sý[ZSûYYRùXXQø\[T÷Z[TöXXQ÷TRN÷MMHþWWRö??9ÿ˜˜–rÿÿÿÿÿÿ••“EZZPÿtslüa`Zÿ]]WÿTTNÿOOJÿNNJÿOOLÿTTRÿUUUÿVUZÿZZm÷]]©ù\ZÍþQPŽøIJWøHHLÿIIHÿKJHÿMMIÿMNIÿ>=9ÿ=<6ÿ==6ÿ??9ÿ770þDC=û»€€€ÿÿÿÿÿÿ€€€311.q$$!„220“775Ÿ;;9«??<¹AA?ÈDDDÑAAAÙ``aë„„‰ÿ{|‹þpp‘ÿlkÿ[ZbÿJKLøHIIêEEC×>=;Ç??<¯€€~š€||{iwvuSpppûF3÷U6&þ9+ÿ,ïfYUŽhdcWZZPQR}}|ÿÿÿýýýúúùÿÿÿccc RdG8ýE,øþH:1ÿH,ý22&þ”%þÿ3$ÿøLJÿáD8ü¢F6÷g=,þ0"ÿöŸ)=<;ÿÿÿýýýùùùÿÿÿ\\]XjL=ÿ@'øMRZþ}zÿE5,þL1#ÿ-,!ÿ°G9ÿÿy~þúszþÿ6*ÿþ4'ÿè:2ý¹I>ötG7þLA3ü9'ÿRNJMûüüýýýúúúÿÿÿqmjGU/ÿE.$÷HNTÿ”©ÿ…›þE1%ÿN6)ÿ,!ÿ¶]PÿÿJOÿóGJÿýŒŽÿþmsþÿ4'ÿÿ3þï/ÿ¬2'ïF$ÿbUN®ÿÿÿýýýýûúswy`A3¬I,ÿPXcö•’«ÿ…‡£ýmiwÿF3&ÿL1%ÿ1."ÿœ-&ÿÿ3&ÿü3+ÿÿW\ÿþptÿø{|ÿúkoýþSZÿÿABút'ÿ53)Æyuvúüûúùù"%'=ÈkhqÿŠ‹ªú“‘¬ÿ][gþ ÿL6'ÿD2%ÿD."ÿ9,!ÿO"ÿ’ÿÔ* ÿû94ÿÿBGÿÿnuþú ÿû}‚úƒQJÿ!Ý,))øùùúúú221P5.ßž™¶ÿ’¬ümitÿþ ÿLHAÿGD?ÿ<@:ÿjC=ÿ‚YTÿKJFÿ--ÿR'ÿŽÿÑ+ÿøCBÿÿRZþ•84ÿ("î=45÷ùùûûý($hPPñ‹¡ÿ214þ214ÿORUÿ,--ÿGJDÿKGBÿ;D?ÿx:9ÿâ65ÿ逸ÿ¾lfÿk2+ÿHL>ÿ:9-ÿ? ÿ“ÿz$þ,$ý5,,%÷ùùøùù920*U>9ÿGGMý9:9ÿTXYÿ?BEÿ@BBÿILDÿKHBÿ=A<ÿy<:ÿÛ*ÿË^ÿÓ|]ÿÑ' ÿž99ÿÕgiÿ¬€|ÿF=.ÿ?A3ú:ÿ641:øùùöøù@83AD2)ÿMRUùUZ\ÿOTVþJMOÿ=?=ÿORLÿKGBÿ=B=ÿA@ÿé-ÿÊŽjÿÒ{[ÿÙ)ÿŽÿÓ(ÿןþÈ5,ÿyHDøFC:ÿ;53Qøùùöö÷FD@ZDCAÿ^gk÷UY[ÿaegþ$&)ÿÿXZSÿHE?ÿAC@ÿ|;6ÿà+ÿ̈_ÿÌ…bÿß+ÿ˜+,ÿÓ( ÿÁ•pþÜ.ÿö@IBÿD?=jö÷÷õõöMNLs`dcÿdknöLOQÿVZ[þ"%ÿ!!ÿXZSÿGFAÿ@>:ÿ{YWÿÙ-)ÿÊŽlÿΊaÿë.ÿŸ&ÿÞ+ÿÊšqþÙ:)ÿ™öCIAÿIECƒö÷÷õõõWYVŒV\\ÿMSWöIMNÿNOQþ""$ÿ>=:ÿ`_YÿNNHÿEB=ÿdfcÿ|z|ÿ‹Šÿ¥‰ÿ³dgÿ_aÿÒ76ÿÆœxþÚC2ÿ›÷EH@ÿOKIœö÷÷õõõ]^[¦231ÿHIKöGGEÿFFBýKJDþUTNþZZSþNNHÿ>?:ÿGB<ÿE=7ÿhcbÿjmkÿM[YþfpnþmsrþŽýžƒ‚ÿ’ln÷PKFÿTTO¶øøøøøø__Z¾::5ÿGGBùQQLÿa`Zþlmeÿ‡ÿÇÈ»ÿŽ…ÿ]]WþUVTþILHþFF@ÿE@;ÿF=7ÿE=7ÿ@83ÿXTQþLPMÿHROúA@;ÿRROÈøøøùùùggaÓUVNÿlleóuumú……{ø‡‡}ù••‹ûœ’üvvmþ‚ƒxÿqqaÿggZÿcc[ÿ[\VþTXQûORLúPSNøKLFöIFAøLGBòAA;ÿ``\Úùùù÷÷÷vvqËrriÿuumúnnfÿde]þYYSÿKJFÿAA@ÿFFFþHGoöPP¡øNNmöPPOüTTPÿUTNÿWVPÿIIBÿFF?þAC<ÿBD?ù771ÿEEA¥øøøøøø;;:.))$430¡00-°775¼=<:ÈFFCÕHHHÞeegñ~~”ÿomµþdd‡ÿJKOüAADí@@?Ü552Éhif³yywœnnkƒpoml``^Hlll ûûûúúú+++ 10EpLK‰¸11O~- ÉÉÉÿÿÿùùùÿÿÿþþþÿÿÿÿÿÿùùù((( 5.gl ÅÅÅÿÿÿ÷÷÷ ÿÿÿ üüûûûûÿÿÿúúúMMM"""000+++,,,---------/.-32: t`â©<8S ,-%,+/231ÐÐÐÿÿÿúúúÿÿÿþþþÿÿÿÿÿÿ(  ÿÿÿ¥¥¤0// /)%QRB8ÞB70§'72,,™œœÿÿÿöõõÿÿÿþþþÿÿÿ™šš  YN6'û9'þI1#ÿ9/û"%´C”ÿÿÿõööÿÿÿþþþÿÿÿœ›1&`N6%ÿG1%ù:4&üo) øÜ=1û“E6ÿM6&ý;C:À[XWP:67žžžÿÿÿÿÿÿ˜š›f\A0ÿFHPùXGHÿ/)ÿˆ7/þÿcgÿøIGøä3'û*ÿW3#ÿɆ‚‚#ÿÿÿýÿÿ—ŒF?ÿZZbø•±ÿgQTÿ+$þ“H?þÿFHÿûlrÿÿlqþû@;ýï>;ñrÿ€{}ÿýþº½¿,‹nfqÿ•˜¶õZWdÿ>,"ýE4'ÿO*ÿ“ÿÌ+"þüNPýûlpÿÿ‰÷²QMÿ ”Á¾¿µ·¸9*,žš²ÿ^]i÷$$$ÿ>92þEIBÿX;3ÿ“b]ÿdOIÿcþŽ$ ÿÎ<;ø¹FBÿ ª¾¼½µ¶¶B10´XVZÿ375ùNTXÿHD@þ@QIÿp,,ÿçF9ÿÞtÿ %þ^WÿxIDúU3(ÿ" À¹¹º¼¼¼(ËW\_ÿX\`û9??ÿLD@þ=ÿÖPBÿÕfÿÏ'þ¸-ÿ×pFý²*ÿ+.+êÀ¾¾ÃÃÂAEC÷RVXÿCEFÿ231ÿKJFÿ@@;ÿOLLþrqqþ“„ÿ—ddÿª^]ÿƇrÿ®KGÿ60,ùÁÁÀ$ÇÇÆ1+,&ôLLHøVVPûmmfü¡¡—üyyqþONBÿMIBÿJNIþ?JGüJWUúbggûliió><7õÅÅÅ;ÌÌËG[[Rÿwxpûyyqÿ~~uþˆˆ~ÿmmjýfetù\]\ûYXPÿXTNÿMG@ÿ=91ÿA@:ü+,%ÿÄÄÃCÃÃÂ993 RRN¾AA=ÇBB@Ö777ÞZYaôjh¯ÿVUxýKLGðBCEÜZ]YÉlnk°lmj¢PPNtÊÊɽ½½$A@iŽ,W ÿÿÿòòòüüýýýý """ B5‹\4™™˜ÿÿÿøøøÿÿÿýýýCubicSDR-0.2.3/icon/NSIS_Header.bmp000066400000000000000000000230161322677621400166020ustar00rootroot00000000000000BM&6(–9Ø!  €€€€€€€€€ÀÀÀÀÜÀðʦ @ ` €   À à@@ @@@`@€@ @À@à`` `@```€` `À`à€€ €@€`€€€ €À€à    @ ` €   À àÀÀ À@À`À€À ÀÀÀààà à@à`à€à àÀàà@@ @@@`@€@ @À@à@ @ @ @@ `@ €@  @ À@ à@@@@ @@@@@`@@€@@ @@À@@à@`@` @`@@``@`€@` @`À@`à@€@€ @€@@€`@€€@€ @€À@€à@ @  @ @@ `@ €@  @ À@ à@À@À @À@@À`@À€@À @ÀÀ@Àà@à@à @à@@à`@à€@à @àÀ@àà€€ €@€`€€€ €À€à€ € € @€ `€ €€  € À€ à€@€@ €@@€@`€@€€@ €@À€@à€`€` €`@€``€`€€` €`À€`à€€€€ €€@€€`€€€€€ €€À€€à€ €  € @€ `€ €€  € À€ à€À€À €À@€À`€À€€À €ÀÀ€Àà€à€à €à@€à`€à€€à €àÀ€ààÀÀ À@À`À€À ÀÀÀàÀ À À @À `À €À  À ÀÀ àÀ@À@ À@@À@`À@€À@ À@ÀÀ@àÀ`À` À`@À``À`€À` À`ÀÀ`àÀ€À€ À€@À€`À€€À€ À€ÀÀ€àÀ À  À @À `À €À  À ÀÀ àÀÀÀÀ ÀÀ@ÀÀ`ÀÀ€ÀÀ ðûÿ¤  €€€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ì£äõ ììììììäãããããÛÛÛÛ›››““RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíQIIIIRì ììììììäãããããÛÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿öööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìQQIIIIIIRãì ìììììäãããããÛÛÛÛ››››“RRR LMV^gooo¯¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷QQQQIIIIIIIR£ì ìììììääããããÛÛÛÛ››››“SRR LMV^ggooo¯¯¯·······¿¿¿¿¿öööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷QQQQQIIIIIIIIIR›äìììììääããããÛÛÛÛÛ›››“’RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬QQQQQQIIIIIIIIIIIQšãììììäãããããÛÛÛÛ›››““RRR LMMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤QQšRQQQIIII‘’QIIIIIIQ’ãììäãããããÛÛÛÛ››››“RRR LMV_gooo¯¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤QRš’QQQQIIII‘ÚÚ’QIIIIIIIRãäãããããÛÛÛÛ››››“RRR LMV^goooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤QRš’IIQQQIIII‰ÒÚÚÚÒQIIIIIIIQÛããããÛÛÛÛÛ›››““RRJ LMUVggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö¤QRš’IIQQQQIII‰ÉÑÒÚÚÚÒ’QIIIIIIQšãããÛÛÛÛ›››““RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¤QRšRIIRQQQQIII‰ÑÑÑÑÒÒÚÚÒ’QIIIIIIQšÛÛÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¤QRšRI[RQQQQIIIÑÑÑÒÚäüÑÒÒÒÒ’‘QIIIIIQ’ÚÛÛ››››“RRR LMV^goooo¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö£Q’’RI[œœRQQQQIIQÒÒãÛ ÛÑÑÑÑÑÒÒÒ’‘QIIIIIQšÛÛ›››““RRJ LMU^ggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£Q’’IIR[÷œœRQQQQIIQÚã ÚäÚÑÑÑÑÑÑÒÒÒÒ‘QIIIIQÚÛ›››““RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›Q’’IR[¤÷¥¥QQQQQIIQÚäÛÛÛäÛÛÛÛÒÑÑÑÑÑÒÒ’‘QIII’Û›››““RRR LLMV_gooo¯¯¯········¿¿¿¿öööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£Q’’IS¤œ¥­¥¤QQQQQIIQÛìÒÑÑÒä Û ÛÑÑÑÑÑÑÑÑ‘‘‘QI’Û››››“RRR LMV^gooo¯¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõR’’IR¤­¤¥¥¥¤IQQQQIIQÒÒÑÑÑÑ ã ãÑÚÑÒÑÑÑÑÑÑQQIQÛ››››“SRR LMV^goooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤’’IIR¤¤¥÷¥œ¤¤IQQQQIIQÑÑÑÑÑÑäÑäÛÛ ÑäÑÒÒÒÑÑIQIQÛÛ›››““RRR LMUVggooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£‘IIR¤¥¥÷¥¤¤¤›QQQQQQIQ‘ÑÑÑÑÑÑÑÚÛÚãÛä ÛäããÑÒQQIQÚÛ›››““RRR LMMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›‘I[œ®¥¥¥¥¤[IQQQQQQIIII‰ÑÑÑÑÑÒÚÛãÒ Û ÒäQQIQšÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿¿öööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš‘›œ¥÷¥¥¥¥[QQQQQIIIIIIII‰‘ÑÑÑÑÑÑÚÒ ä ää RQQQšÛ››››“RRR LMV^goooo¯¯¯·······¿¿¿¿¿öööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ𒤥÷¥÷¥¤œIQQRRRRRQQIIIIIIII‰‘ÑÑÑÑÑäÒäÚãã‘QQQšÛÛ›››““RRJ LMUVggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõš’¥÷¥¥÷œ[JRIIRRRRRRRRšš’IIIIIIIII‰ÑÑÒÑÒããÒ‘QQQšÛÛ›››““RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõš’®÷¥÷¥RRIRIRRRRRRRRšÛäíå›IIIIIIIII‰ÑÑÒÒщQQI’ÛÛ››››“RRR LLMV_gooo¯¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí’›¥¥÷RIRR¤[IRRRRRRRR’‰ÒÛ ö Û’QIIIIIIII‰ÑщIQI’ÛÛ››››“RRR LMV^goooo¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷’¤¥¤RRI¤[IRRRRRRRR’ÉÉÈüõÒÒÚ’’QQQIIIIIIIIII’ÛÛ››››““RRJ LMV^ggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì’¤›[R¤RI›RQZRRRRRRR’ÉÉÉüó~üÑÑ’ÒÛÛÛ›RQIIIIIII’ÛÛÛ›››““RRR LMUV_gooo¯¯¯·······¿¿¿¿¿öööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤’R[RRI›SRRIIRZRRRRRRR’ÉÉÉüó~ÐÉÉɉ‘Òä Û’RQIIII’ÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö£RIIR[¤R[R[I¤RRRRRRRRR’ÉÉÉüó~ÐÉÉɉ‰ÉÉÚ íÚšššRQI‘ÚÛÛ››››“RRR LMV^goooo¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ £QI¤IR›R[RR¤RRRZRRRRRRR’ÉÉÉüóÐÉÉɉ‰ÉÉüëµÑÑ’š›ZRRÚÛÛ››››“SRR LMV^ggooo¯¯¯·······¿¿¿¿¿öööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ›RI¤RR[[R[›RRZZRRRRRR’ÉÉÑüóÐÉÉɉ‰ÉÉÑëµüÉɉ’ZRRÚÛÛÛ›››““RRR LMUVggooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ›ZI[[[R›[[[RZZRRRRRR’ÉÉÉüóØüÉÉɉ‰ÉÑëµÑÉɉ’RRRšÛÛÛ›››““RRR LMMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›RIS¤[›[R›[RZZRRRRRR’ÉÉÉüëÐÉÉÉɉÉÉÉãµÑÉɉ‘RRRšÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ[R[[÷[[RR›[RZZZRRRRR’ÉÉÉüëàüÉɉ‰ÉÉÑâ½ÙÉɉ‘RRRšÛÛÛ››››“RRR LMV^goooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ[[¤›[R[[J[[ZZZZRRRRR’ÑÑÑüòØÉÉÉÉÉÉÉÉâ½ÙÉɉ‰RRRšÛÛÛÛ›››““RRJ LMU^ggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ[¤[R[R[[I›RI[ZZZRRRRR¤÷¤ãÚëÙÑÉÈÉÉÉÉÉâ½ÚÉɉ‰RRRšÛÛÛÛ›››““RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷[¤RR¤›[I[RIII[[[ZRRRRR›¤¤¤­÷­ääãÚÒÒÉÈÈÚ½ÚÉɉ‰RRRšÛÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö÷[[IR¤¤JRRIIIQ[[[ZRRRRR[[[[›¤›­¤¤÷÷¤ääÛã½âÈȉ‰RRR’ÛÛÛÛ››››“RRR LMV^gooo¯¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤[R¤RI¤IRRRRRZ[[[[ZRRRRRZ››[›[[[››¤¤¤¤÷¬÷ìãÚ’RRR’ÛÛÛÛ››››“’RR LMU^ggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¤[R¤IRRRRRZZ[[››››[RRRRRRRR[›÷Z[[[[[[[››¤¤¤¤÷¤ZRRRÛÛÛÛÛ›››““RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ZRI[IRZRZ[[›››£¤¤¤›ZRRRRRRRRRRRRRRRRZ›[[¤¤[[››RRRRÛÛÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›RIIRRZ[[[››››£¬õ÷õ¤›[ZRRRRRRRRRRRRRRRRR[››ZZRRRRRRÛÛÛÛÛ››››“RRR LLMV_goooo¯¯········¿¿¿¿¿öööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZRRRZ[[››››£¤¤¬ö÷¤¤››[ZZZRRRRRRRRRRRRRRRRRRRRRRRÛÛÛÛÛ››››“RRR LMV^ggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRZ[[[›››£¤¤¤¬¬÷õ÷¬¤¤÷÷¤¤£›››[[ZZRRRRRRRRRRRRRRRRRRÛÛÛÛÛÛ›››““RRR LMUVggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZ[›››££¤¤¤¤¤¤¤¤£››[[[›››››››››››››[[ZZZRRRRRRRRRRRRÛãÛÛÛÛ›››““RRR LMMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[››££¤¤¤¤£›››[[ZZRRRRSSSSSZZZ[[[[[[[[[[[[ZZZRRRRRRRÛãÛÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›££££›››[[ZZZRRRRRRR[ST^^UTSSRRRRRRRRRRZZZZZZZZRRRšããÛÛÛÛ››››“SRR LMV^goooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬››[[ZZZZRRRZZZZ[RSS[\]^_^]\[SSRRRRRRRRRRRRRRRRRRšãããÛÛÛÛÛ›››““RRJ LMUVggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ õ£[ZZZZZZZRRRRRSS[¤¬÷÷¤¤¤œ›[RRSRRRRRRRRRZ››››£ãããããããÛÛÛÛ›››““RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöõõõ÷÷¬¤¤£›››œœœœœ[[[RZ[››£¤÷ìììõ ììììììäãããããÛÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿öööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷œž_^\œ÷ íìììììäãããããÛÛÛÛ››››“SRR LMV^goooo¯¯········¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥îÿÿ ìììììäãããããÛÛÛÛ››››“’RR LMV^ggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿÿÿ ììììììäããããÛÛÛÛÛ›››““RRR LMUV_gooo¯¯¯········¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöîÿÿÿÿ ììììììäãããããÛÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿öööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïŸïÿÿÿÿ ìììììäãããããÛÛÛÛ›››““RRR LLMV_gooo¯¯¯········¿¿¿¿¿öööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö§Ÿïÿÿÿÿ ìììììäãããããÛÛÛÛ››››“RRR LMV^goooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿÿÿÿ ìììììääããããÛÛÛÛÛ›››““RRR LMUVggooo¯¯¯·······¿¿¿¿¿ööööööööööööööööööööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿCubicSDR-0.2.3/src/000077500000000000000000000000001322677621400137135ustar00rootroot00000000000000CubicSDR-0.2.3/src/AppConfig.cpp000066400000000000000000000664771322677621400163110ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "AppConfig.h" #include "CubicSDR.h" #include DeviceConfig::DeviceConfig() : deviceId("") { ppm.store(0); offset.store(0); agcMode.store(true); sampleRate.store(0); } DeviceConfig::DeviceConfig(std::string deviceId) : DeviceConfig() { this->deviceId = deviceId; } void DeviceConfig::setPPM(int ppm) { this->ppm.store(ppm); } int DeviceConfig::getPPM() { return ppm.load(); } void DeviceConfig::setOffset(long long offset) { this->offset.store(offset); } long long DeviceConfig::getOffset() { return offset.load(); } void DeviceConfig::setSampleRate(long srate) { sampleRate.store(srate); } long DeviceConfig::getSampleRate() { return sampleRate.load(); } void DeviceConfig::setAntennaName(const std::string& name) { antennaName = name; } const std::string& DeviceConfig::getAntennaName() { return antennaName; } void DeviceConfig::setAGCMode(bool agcMode) { this->agcMode.store(agcMode); } bool DeviceConfig::getAGCMode() { return agcMode.load(); } void DeviceConfig::setDeviceId(std::string deviceId) { std::lock_guard < std::mutex > lock(busy_lock); this->deviceId = deviceId; } std::string DeviceConfig::getDeviceId() { std::string tmp; std::lock_guard < std::mutex > lock(busy_lock); tmp = deviceId; return tmp; } void DeviceConfig::setDeviceName(std::string deviceName) { std::lock_guard < std::mutex > lock(busy_lock); this->deviceName = deviceName; } std::string DeviceConfig::getDeviceName() { std::string tmp; std::lock_guard < std::mutex > lock(busy_lock); tmp = (deviceName=="")?deviceId:deviceName; return tmp; } void DeviceConfig::save(DataNode *node) { std::lock_guard < std::mutex > lock(busy_lock); *node->newChild("id") = deviceId; *node->newChild("name") = deviceName; *node->newChild("ppm") = ppm.load(); *node->newChild("offset") = offset.load(); *node->newChild("sample_rate") = sampleRate.load(); *node->newChild("agc_mode") = agcMode.load()?1:0; if (!antennaName.empty()) { *node->newChild("antenna") = antennaName; } if (streamOpts.size()) { DataNode *streamOptsNode = node->newChild("streamOpts"); for (ConfigSettings::const_iterator opt_i = streamOpts.begin(); opt_i != streamOpts.end(); opt_i++) { *streamOptsNode->newChild(opt_i->first.c_str()) = opt_i->second; } } if (settings.size()) { DataNode *settingsNode = node->newChild("settings"); for (ConfigSettings::const_iterator set_i = settings.begin(); set_i != settings.end(); set_i++) { *settingsNode->newChild(set_i->first.c_str()) = set_i->second; } } if (rigIF.size()) { DataNode *rigIFs = node->newChild("rig_ifs"); for (std::map::const_iterator rigIF_i = rigIF.begin(); rigIF_i != rigIF.end(); rigIF_i++) { DataNode *ifNode = rigIFs->newChild("rig_if"); *ifNode->newChild("model") = rigIF_i->first; *ifNode->newChild("sdr_if") = rigIF_i->second; } } if (gains.size()) { DataNode *gainsNode = node->newChild("gains"); for (ConfigGains::const_iterator gain_i = gains.begin(); gain_i != gains.end(); gain_i++) { DataNode *gainNode = gainsNode->newChild("gain"); *gainNode->newChild("id") = gain_i->first; *gainNode->newChild("value") = gain_i->second; } } } void DeviceConfig::load(DataNode *node) { std::lock_guard < std::mutex > lock(busy_lock); if (node->hasAnother("name")) { deviceName = node->getNext("name")->element()->toString(); } if (node->hasAnother("ppm")) { DataNode *ppm_node = node->getNext("ppm"); int ppmValue = 0; ppm_node->element()->get(ppmValue); setPPM(ppmValue); } if (node->hasAnother("offset")) { DataNode *offset_node = node->getNext("offset"); long long offsetValue = 0; offset_node->element()->get(offsetValue); setOffset(offsetValue); } if (node->hasAnother("agc_mode")) { DataNode *agc_node = node->getNext("agc_mode"); int agcModeValue = 0; agc_node->element()->get(agcModeValue); setAGCMode(agcModeValue?true:false); } if (node->hasAnother("sample_rate")) { DataNode *sample_rate_node = node->getNext("sample_rate"); long sampleRateValue = 0; sample_rate_node->element()->get(sampleRateValue); setSampleRate(sampleRateValue); } if (node->hasAnother("antenna")) { DataNode *antenna_node = node->getNext("antenna"); std::string antennaNameValue; antenna_node->element()->get(antennaNameValue); setAntennaName(antennaNameValue); } if (node->hasAnother("streamOpts")) { DataNode *streamOptsNode = node->getNext("streamOpts"); for (int i = 0, iMax = streamOptsNode->numChildren(); ichild(i); std::string keyName = streamOptNode->getName(); std::string strSettingValue = streamOptNode->element()->toString(); if (keyName != "") { setStreamOpt(keyName, strSettingValue); } } } if (node->hasAnother("settings")) { DataNode *settingsNode = node->getNext("settings"); for (int i = 0, iMax = settingsNode->numChildren(); ichild(i); std::string keyName = settingNode->getName(); std::string strSettingValue = settingNode->element()->toString(); if (keyName != "") { setSetting(keyName, strSettingValue); } } } if (node->hasAnother("rig_ifs")) { DataNode *rigIFNodes = node->getNext("rig_ifs"); while (rigIFNodes->hasAnother("rig_if")) { DataNode *rigIFNode = rigIFNodes->getNext("rig_if"); if (rigIFNode->hasAnother("model") && rigIFNode->hasAnother("sdr_if")) { int load_model; long long load_freq; rigIFNode->getNext("model")->element()->get(load_model); rigIFNode->getNext("sdr_if")->element()->get(load_freq); rigIF[load_model] = load_freq; } } } if (node->hasAnother("gains")) { DataNode *gainsNode = node->getNext("gains"); while (gainsNode->hasAnother("gain")) { DataNode *gainNode = gainsNode->getNext("gain"); std::string keyName; float fltSettingValue; gainNode->getNext("id")->element()->get(keyName); gainNode->getNext("value")->element()->get(fltSettingValue); if (keyName != "" && !(fltSettingValue!=fltSettingValue)) { setGain(keyName, fltSettingValue); } } } } void DeviceConfig::setStreamOpts(ConfigSettings opts) { streamOpts = opts; } ConfigSettings DeviceConfig::getStreamOpts() { return streamOpts; } void DeviceConfig::setStreamOpt(std::string key, std::string value) { streamOpts[key] = value; } std::string DeviceConfig::getStreamOpt(std::string key, std::string defaultValue) { if (streamOpts.find(key) == streamOpts.end()) { return defaultValue; } return streamOpts[key]; } void DeviceConfig::setSettings(ConfigSettings settings) { this->settings = settings; } void DeviceConfig::setSetting(std::string key, std::string value) { this->settings[key] = value; } std::string DeviceConfig::getSetting(std::string key, std::string defaultValue) { if (settings.find(key) == settings.end()) { return defaultValue; } return settings[key]; } ConfigSettings DeviceConfig::getSettings() { return settings; } void DeviceConfig::setGains(ConfigGains gains) { this->gains = gains; } ConfigGains DeviceConfig::getGains() { return gains; } void DeviceConfig::setGain(std::string key, float value) { gains[key] = value; } float DeviceConfig::getGain(std::string key, float defaultValue) { if (gains.find(key) != gains.end()) { return gains[key]; } return defaultValue; } void DeviceConfig::setRigIF(int rigType, long long freq) { rigIF[rigType] = freq; } long long DeviceConfig::getRigIF(int rigType) { if (rigIF.find(rigType) != rigIF.end()) { return rigIF[rigType]; } return 0; } AppConfig::AppConfig() : configName("") { winX.store(0); winY.store(0); winW.store(0); winH.store(0); winMax.store(false); showTips.store(true); lowPerfMode.store(false); themeId.store(0); fontScale.store(0); snap.store(1); centerFreq.store(100000000); waterfallLinesPerSec.store(DEFAULT_WATERFALL_LPS); spectrumAvgSpeed.store(0.65f); dbOffset.store(0); modemPropsCollapsed.store(false); mainSplit = -1; visSplit = -1; bookmarkSplit = 200; #ifdef CUBICSDR_DEFAULT_HIDE_BOOKMARKS bookmarksVisible.store(false); #else bookmarksVisible.store(true); #endif #ifdef USE_HAMLIB rigEnabled.store(false); rigModel.store(1); rigRate.store(57600); rigPort = "/dev/ttyUSB0"; rigControlMode.store(true); rigFollowMode.store(true); #endif } DeviceConfig *AppConfig::getDevice(std::string deviceId) { if (deviceConfig.find(deviceId) == deviceConfig.end()) { deviceConfig[deviceId] = new DeviceConfig(); } DeviceConfig *conf = deviceConfig[deviceId]; conf->setDeviceId(deviceId); return conf; } std::string AppConfig::getConfigDir() { std::string dataDir = wxStandardPaths::Get().GetUserDataDir().ToStdString(); bool mkStatus = false; if (!wxDir::Exists(dataDir)) { mkStatus = wxDir::Make(dataDir); } else { mkStatus = true; } if (!mkStatus) { std::cout << "Warning, unable to initialize user data directory." << std::endl; } return dataDir; } void AppConfig::setWindow(wxPoint winXY, wxSize winWH) { winX.store(winXY.x); winY.store(winXY.y); winW.store(winWH.x); winH.store(winWH.y); } void AppConfig::setWindowMaximized(bool max) { winMax.store(max); } bool AppConfig::getWindowMaximized() { return winMax.load(); } void AppConfig::setModemPropsCollapsed(bool collapse) { modemPropsCollapsed.store(collapse); } bool AppConfig::getModemPropsCollapsed() { return modemPropsCollapsed.load(); } void AppConfig::setShowTips(bool show) { showTips.store(show); } bool AppConfig::getShowTips() { return showTips.load(); } void AppConfig::setLowPerfMode(bool show) { lowPerfMode.store(show); } bool AppConfig::getLowPerfMode() { return lowPerfMode.load(); } wxRect *AppConfig::getWindow() { wxRect *r = NULL; if (winH.load() && winW.load()) { r = new wxRect(winX.load(),winY.load(),winW.load(),winH.load()); } return r; } void AppConfig::setTheme(int themeId) { this->themeId.store(themeId); } int AppConfig::getTheme() { return themeId.load(); } void AppConfig::setFontScale(int fontScale) { this->fontScale.store(fontScale); } int AppConfig::getFontScale() { return fontScale.load(); } void AppConfig::setSnap(long long snapVal) { this->snap.store(snapVal); } long long AppConfig::getSnap() { return snap.load(); } void AppConfig::setCenterFreq(long long freqVal) { centerFreq.store(freqVal); } long long AppConfig::getCenterFreq() { return centerFreq.load(); } void AppConfig::setWaterfallLinesPerSec(int lps) { waterfallLinesPerSec.store(lps); } int AppConfig::getWaterfallLinesPerSec() { return waterfallLinesPerSec.load(); } void AppConfig::setSpectrumAvgSpeed(float avgSpeed) { spectrumAvgSpeed.store(avgSpeed); } float AppConfig::getSpectrumAvgSpeed() { return spectrumAvgSpeed.load(); } void AppConfig::setDBOffset(int offset) { this->dbOffset.store(offset); } int AppConfig::getDBOffset() { return dbOffset.load(); } void AppConfig::setManualDevices(std::vector manuals) { manualDevices = manuals; } std::vector AppConfig::getManualDevices() { return manualDevices; } void AppConfig::setMainSplit(float value) { mainSplit.store(value); } float AppConfig::getMainSplit() { return mainSplit.load(); } void AppConfig::setVisSplit(float value) { visSplit.store(value); } float AppConfig::getVisSplit() { return visSplit.load(); } void AppConfig::setBookmarkSplit(float value) { bookmarkSplit.store(value); } float AppConfig::getBookmarkSplit() { return bookmarkSplit.load(); } void AppConfig::setBookmarksVisible(bool state) { bookmarksVisible.store(state); } bool AppConfig::getBookmarksVisible() { return bookmarksVisible.load(); } void AppConfig::setRecordingPath(std::string recPath) { recordingPath = recPath; } std::string AppConfig::getRecordingPath() { return recordingPath; } bool AppConfig::verifyRecordingPath() { string recPathStr = wxGetApp().getConfig()->getRecordingPath(); if (recPathStr.empty()) { wxMessageBox( wxT("Recording path is not set. Please use 'Set Recording Path' from the 'File' Menu."), wxT("Recording Path Error"), wxICON_INFORMATION); return false; } wxFileName recPath(recPathStr); if (!recPath.Exists() || !recPath.IsDirWritable()) { wxMessageBox( wxT("Recording path does not exist or is not writable. Please use 'Set Recording Path' from the 'File' Menu."), wxT("Recording Path Error"), wxICON_INFORMATION); return false; } return true; } void AppConfig::setRecordingSquelchOption(int enumChoice) { recordingSquelchOption = enumChoice; } int AppConfig::getRecordingSquelchOption() { return recordingSquelchOption; } void AppConfig::setRecordingFileTimeLimit(int nbSeconds) { recordingFileTimeLimitSeconds = nbSeconds; } int AppConfig::getRecordingFileTimeLimit() { return recordingFileTimeLimitSeconds; } void AppConfig::setConfigName(std::string configName) { this->configName = configName; } std::string AppConfig::getConfigFileName(bool ignoreName) { std::string cfgFileDir = getConfigDir(); wxFileName cfgFile; if (configName.length() && !ignoreName) { std::string tempFn("config-"); tempFn.append(configName); tempFn.append(".xml"); cfgFile = wxFileName(cfgFileDir, tempFn); } else { cfgFile = wxFileName(cfgFileDir, "config.xml"); } std::string cfgFileName = cfgFile.GetFullPath(wxPATH_NATIVE).ToStdString(); return cfgFileName; } bool AppConfig::save() { DataTree cfg; cfg.rootNode()->setName("cubicsdr_config"); if (winW.load() && winH.load()) { DataNode *window_node = cfg.rootNode()->newChild("window"); *window_node->newChild("x") = winX.load(); *window_node->newChild("y") = winY.load(); *window_node->newChild("w") = winW.load(); *window_node->newChild("h") = winH.load(); *window_node->newChild("max") = winMax.load(); *window_node->newChild("tips") = showTips.load(); *window_node->newChild("low_perf_mode") = lowPerfMode.load(); *window_node->newChild("theme") = themeId.load(); *window_node->newChild("font_scale") = fontScale.load(); *window_node->newChild("snap") = snap.load(); *window_node->newChild("center_freq") = centerFreq.load(); *window_node->newChild("waterfall_lps") = waterfallLinesPerSec.load(); *window_node->newChild("spectrum_avg") = spectrumAvgSpeed.load(); *window_node->newChild("modemprops_collapsed") = modemPropsCollapsed.load();; *window_node->newChild("db_offset") = dbOffset.load(); *window_node->newChild("main_split") = mainSplit.load(); *window_node->newChild("vis_split") = visSplit.load(); *window_node->newChild("bookmark_split") = bookmarkSplit.load(); *window_node->newChild("bookmark_visible") = bookmarksVisible.load(); } //Recording settings: DataNode *rec_node = cfg.rootNode()->newChild("recording"); *rec_node->newChild("path") = recordingPath; *rec_node->newChild("squelch") = recordingSquelchOption; *rec_node->newChild("file_time_limit") = recordingFileTimeLimitSeconds; DataNode *devices_node = cfg.rootNode()->newChild("devices"); std::map::iterator device_config_i; for (device_config_i = deviceConfig.begin(); device_config_i != deviceConfig.end(); device_config_i++) { DataNode *device_node = devices_node->newChild("device"); device_config_i->second->save(device_node); } if (manualDevices.size()) { DataNode *manual_node = cfg.rootNode()->newChild("manual_devices"); for (std::vector::const_iterator i = manualDevices.begin(); i != manualDevices.end(); i++) { DataNode *rig_node = manual_node->newChild("device"); *rig_node->newChild("factory") = i->factory; *rig_node->newChild("params") = i->params; } } #ifdef USE_HAMLIB DataNode *rig_node = cfg.rootNode()->newChild("rig"); *rig_node->newChild("enabled") = rigEnabled.load()?1:0; *rig_node->newChild("model") = rigModel.load(); *rig_node->newChild("rate") = rigRate.load(); *rig_node->newChild("port") = rigPort; *rig_node->newChild("control") = rigControlMode.load()?1:0; *rig_node->newChild("follow") = rigFollowMode.load()?1:0; *rig_node->newChild("center_lock") = rigCenterLock.load()?1:0; *rig_node->newChild("follow_modem") = rigFollowModem.load()?1:0; #endif std::string cfgFileName = getConfigFileName(); if (!cfg.SaveToFileXML(cfgFileName)) { std::cout << "Error saving :: configuration file '" << cfgFileName << "' is not writable!" << std::endl; return false; } return true; } bool AppConfig::load() { DataTree cfg; std::string cfgFileDir = getConfigDir(); std::string cfgFileName = getConfigFileName(); wxFileName cfgFile = wxFileName(cfgFileName); if (!cfgFile.Exists()) { if (configName.length()) { wxFileName baseConfig = wxFileName(getConfigFileName(true)); if (baseConfig.Exists()) { std::string baseConfigFileName = baseConfig.GetFullPath(wxPATH_NATIVE).ToStdString(); std::cout << "Creating new configuration file '" << cfgFileName << "' by copying '" << baseConfigFileName << "'.."; wxCopyFile(baseConfigFileName, cfgFileName); if (!cfgFile.Exists()) { std::cout << "failed." << std::endl; return true; } std::cout << "ok." << std::endl; } else { return true; } } else { return true; } } if (cfgFile.IsFileReadable()) { std::cout << "Loading:: configuration file '" << cfgFileName << "'" << std::endl; cfg.LoadFromFileXML(cfgFileName); } else { std::cout << "Error loading:: configuration file '" << cfgFileName << "' is not readable!" << std::endl; return false; } if (cfg.rootNode()->hasAnother("window")) { int x = 0 ,y = 0 ,w = 0 ,h = 0; int max = 0 ,tips = 0 ,lpm = 0 ,mpc = 0; DataNode *win_node = cfg.rootNode()->getNext("window"); if (win_node->hasAnother("w") && win_node->hasAnother("h") && win_node->hasAnother("x") && win_node->hasAnother("y")) { win_node->getNext("x")->element()->get(x); win_node->getNext("y")->element()->get(y); win_node->getNext("w")->element()->get(w); win_node->getNext("h")->element()->get(h); winX.store(x); winY.store(y); winW.store(w); winH.store(h); } if (win_node->hasAnother("max")) { win_node->getNext("max")->element()->get(max); winMax.store(max?true:false); } if (win_node->hasAnother("tips")) { win_node->getNext("tips")->element()->get(tips); showTips.store(tips?true:false); } if (win_node->hasAnother("low_perf_mode")) { win_node->getNext("low_perf_mode")->element()->get(lpm); lowPerfMode.store(lpm?true:false); } if (win_node->hasAnother("theme")) { int theme; win_node->getNext("theme")->element()->get(theme); themeId.store(theme); } if (win_node->hasAnother("font_scale")) { int fscale; win_node->getNext("font_scale")->element()->get(fscale); fontScale.store(fscale); } if (win_node->hasAnother("snap")) { long long snapVal; win_node->getNext("snap")->element()->get(snapVal); snap.store(snapVal); } if (win_node->hasAnother("center_freq")) { long long freqVal; win_node->getNext("center_freq")->element()->get(freqVal); centerFreq.store(freqVal); } if (win_node->hasAnother("waterfall_lps")) { int lpsVal; win_node->getNext("waterfall_lps")->element()->get(lpsVal); waterfallLinesPerSec.store(lpsVal); } if (win_node->hasAnother("spectrum_avg")) { float avgVal; win_node->getNext("spectrum_avg")->element()->get(avgVal); spectrumAvgSpeed.store(avgVal); } if (win_node->hasAnother("modemprops_collapsed")) { win_node->getNext("modemprops_collapsed")->element()->get(mpc); modemPropsCollapsed.store(mpc?true:false); } if (win_node->hasAnother("db_offset")) { DataNode *offset_node = win_node->getNext("db_offset"); int offsetValue = 0; offset_node->element()->get(offsetValue); setDBOffset(offsetValue); } if (win_node->hasAnother("main_split")) { float gVal; win_node->getNext("main_split")->element()->get(gVal); mainSplit.store(gVal); } if (win_node->hasAnother("vis_split")) { float gVal; win_node->getNext("vis_split")->element()->get(gVal); visSplit.store(gVal); } if (win_node->hasAnother("bookmark_split")) { float gVal; win_node->getNext("bookmark_split")->element()->get(gVal); bookmarkSplit.store(gVal); } if (win_node->hasAnother("bookmark_visible")) { int bVal; win_node->getNext("bookmark_visible")->element()->get(bVal); bookmarksVisible.store(bVal); } } //Recording settings: if (cfg.rootNode()->hasAnother("recording")) { DataNode *rec_node = cfg.rootNode()->getNext("recording"); if (rec_node->hasAnother("path")) { DataNode *rec_path = rec_node->getNext("path"); recordingPath = rec_path->element()->toString(); } if (rec_node->hasAnother("squelch")) { DataNode *rec_squelch = rec_node->getNext("squelch"); rec_squelch->element()->get(recordingSquelchOption); } if (rec_node->hasAnother("file_time_limit")) { DataNode *rec_file_time_limit = rec_node->getNext("file_time_limit"); rec_file_time_limit->element()->get(recordingFileTimeLimitSeconds); } } if (cfg.rootNode()->hasAnother("devices")) { DataNode *devices_node = cfg.rootNode()->getNext("devices"); while (devices_node->hasAnother("device")) { DataNode *device_node = devices_node->getNext("device"); if (device_node->hasAnother("id")) { std::string deviceId = device_node->getNext("id")->element()->toString(); getDevice(deviceId)->load(device_node); } } } if (cfg.rootNode()->hasAnother("manual_devices")) { DataNode *manuals_node = cfg.rootNode()->getNext("manual_devices"); while (manuals_node->hasAnother("device")) { DataNode *manual_node = manuals_node->getNext("device"); if (manual_node->hasAnother("factory") && manual_node->hasAnother("params")) { SDRManualDef mdef; mdef.factory = manual_node->getNext("factory")->element()->toString(); mdef.params = manual_node->getNext("params")->element()->toString(); manualDevices.push_back(mdef); } } } #ifdef USE_HAMLIB if (cfg.rootNode()->hasAnother("rig")) { DataNode *rig_node = cfg.rootNode()->getNext("rig"); if (rig_node->hasAnother("enabled")) { int loadEnabled; rig_node->getNext("enabled")->element()->get(loadEnabled); rigEnabled.store(loadEnabled?true:false); } if (rig_node->hasAnother("model")) { int loadModel; rig_node->getNext("model")->element()->get(loadModel); rigModel.store(loadModel?loadModel:1); } if (rig_node->hasAnother("rate")) { int loadRate; rig_node->getNext("rate")->element()->get(loadRate); rigRate.store(loadRate?loadRate:57600); } if (rig_node->hasAnother("port")) { rigPort = rig_node->getNext("port")->element()->toString(); } if (rig_node->hasAnother("control")) { int loadControl; rig_node->getNext("control")->element()->get(loadControl); rigControlMode.store(loadControl?true:false); } if (rig_node->hasAnother("follow")) { int loadFollow; rig_node->getNext("follow")->element()->get(loadFollow); rigFollowMode.store(loadFollow?true:false); } if (rig_node->hasAnother("center_lock")) { int loadCenterLock; rig_node->getNext("center_lock")->element()->get(loadCenterLock); rigCenterLock.store(loadCenterLock?true:false); } if (rig_node->hasAnother("follow_modem")) { int loadFollow; rig_node->getNext("follow_modem")->element()->get(loadFollow); rigFollowModem.store(loadFollow?true:false); } } #endif return true; } bool AppConfig::reset() { return true; } #if USE_HAMLIB int AppConfig::getRigModel() { return rigModel.load(); } void AppConfig::setRigModel(int rigModel) { this->rigModel.store(rigModel); } int AppConfig::getRigRate() { return rigRate.load(); } void AppConfig::setRigRate(int rigRate) { this->rigRate.store(rigRate); } std::string AppConfig::getRigPort() { return rigPort; } void AppConfig::setRigPort(std::string rigPort) { this->rigPort = rigPort; } void AppConfig::setRigControlMode(bool cMode) { rigControlMode.store(cMode); } bool AppConfig::getRigControlMode() { return rigControlMode.load(); } void AppConfig::setRigFollowMode(bool fMode) { rigFollowMode.store(fMode); } bool AppConfig::getRigFollowMode() { return rigFollowMode.load(); } void AppConfig::setRigCenterLock(bool cLock) { rigCenterLock.store(cLock); } bool AppConfig::getRigCenterLock() { return rigCenterLock.load(); } void AppConfig::setRigFollowModem(bool fMode) { rigFollowModem.store(fMode); } bool AppConfig::getRigFollowModem() { return rigFollowModem.load(); } void AppConfig::setRigEnabled(bool enabled) { rigEnabled.store(enabled); } bool AppConfig::getRigEnabled() { return rigEnabled.load(); } #endif CubicSDR-0.2.3/src/AppConfig.h000066400000000000000000000124241322677621400157350ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include #include #include "DataTree.h" #include "CubicSDRDefs.h" #include "SDRDeviceInfo.h" typedef std::map ConfigSettings; typedef std::map ConfigGains; class DeviceConfig { public: DeviceConfig(); DeviceConfig(std::string deviceId); void setPPM(int ppm); int getPPM(); void setOffset(long long offset); long long getOffset(); void setSampleRate(long srate); long getSampleRate(); void setAntennaName(const std::string& name); const std::string& getAntennaName(); void setAGCMode(bool agcMode); bool getAGCMode(); void setDeviceId(std::string deviceId); std::string getDeviceId(); void setDeviceName(std::string deviceName); std::string getDeviceName(); void setStreamOpts(ConfigSettings opts); ConfigSettings getStreamOpts(); void setStreamOpt(std::string key, std::string value); std::string getStreamOpt(std::string key, std::string defaultValue); void setSettings(ConfigSettings settings); ConfigSettings getSettings(); void setSetting(std::string key, std::string value); std::string getSetting(std::string key, std::string defaultValue); void setGains(ConfigGains gains); ConfigGains getGains(); void setGain(std::string key, float value); float getGain(std::string key, float defaultValue); void setRigIF(int rigType, long long freq); long long getRigIF(int rigType); void save(DataNode *node); void load(DataNode *node); private: std::string deviceId; std::string deviceName; std::mutex busy_lock; std::atomic_int ppm; std::atomic_llong offset; std::atomic_bool agcMode; std::atomic_long sampleRate; std::string antennaName; ConfigSettings streamOpts; ConfigGains gains; std::map settings; std::map rigIF; }; class AppConfig { public: AppConfig(); std::string getConfigDir(); DeviceConfig *getDevice(std::string deviceId); void setWindow(wxPoint winXY, wxSize winWH); wxRect *getWindow(); void setWindowMaximized(bool max); bool getWindowMaximized(); void setModemPropsCollapsed(bool collapse); bool getModemPropsCollapsed(); void setShowTips(bool show); bool getShowTips(); void setLowPerfMode(bool lpm); bool getLowPerfMode(); void setTheme(int themeId); int getTheme(); void setFontScale(int scaleValue); int getFontScale(); void setSnap(long long snapVal); long long getSnap(); void setCenterFreq(long long freqVal); long long getCenterFreq(); void setWaterfallLinesPerSec(int lps); int getWaterfallLinesPerSec(); void setSpectrumAvgSpeed(float avgSpeed); float getSpectrumAvgSpeed(); void setDBOffset(int offset); int getDBOffset(); void setManualDevices(std::vector manuals); std::vector getManualDevices(); void setMainSplit(float value); float getMainSplit(); void setVisSplit(float value); float getVisSplit(); void setBookmarkSplit(float value); float getBookmarkSplit(); void setBookmarksVisible(bool state); bool getBookmarksVisible(); //Recording settings: void setRecordingPath(std::string recPath); std::string getRecordingPath(); bool verifyRecordingPath(); void setRecordingSquelchOption(int enumChoice); int getRecordingSquelchOption(); void setRecordingFileTimeLimit(int nbSeconds); int getRecordingFileTimeLimit(); #if USE_HAMLIB int getRigModel(); void setRigModel(int rigModel); int getRigRate(); void setRigRate(int rigRate); std::string getRigPort(); void setRigPort(std::string rigPort); void setRigControlMode(bool cMode); bool getRigControlMode(); void setRigFollowMode(bool fMode); bool getRigFollowMode(); void setRigCenterLock(bool cLock); bool getRigCenterLock(); void setRigFollowModem(bool fMode); bool getRigFollowModem(); void setRigEnabled(bool enabled); bool getRigEnabled(); #endif void setConfigName(std::string configName); std::string getConfigFileName(bool ignoreName=false); bool save(); bool load(); bool reset(); private: std::string configName; std::map deviceConfig; std::atomic_int winX,winY,winW,winH; std::atomic_bool winMax, showTips, lowPerfMode, modemPropsCollapsed; std::atomic_int themeId; std::atomic_int fontScale; std::atomic_llong snap; std::atomic_llong centerFreq; std::atomic_int waterfallLinesPerSec; std::atomic spectrumAvgSpeed, mainSplit, visSplit, bookmarkSplit; std::atomic_int dbOffset; std::vector manualDevices; std::atomic_bool bookmarksVisible; std::string recordingPath = ""; int recordingSquelchOption = 0; int recordingFileTimeLimitSeconds = 0; #if USE_HAMLIB std::atomic_int rigModel, rigRate; std::string rigPort; std::atomic_bool rigEnabled, rigFollowMode, rigControlMode, rigCenterLock, rigFollowModem; #endif }; CubicSDR-0.2.3/src/AppFrame.cpp000066400000000000000000003332551322677621400161250ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "AppFrame.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #include "wx/numdlg.h" #include "wx/filedlg.h" #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include #include #include "AudioSinkFileThread.h" #include "CubicSDR.h" #include "DataTree.h" #include "ColorTheme.h" #include "DemodulatorMgr.h" #include "ImagePanel.h" #include "ActionDialog.h" #include #include #include #include #include #include #ifdef __linux__ #include "CubicSDR.xpm" #endif wxBEGIN_EVENT_TABLE(AppFrame, wxFrame) //EVT_MENU(wxID_NEW, AppFrame::OnNewWindow) EVT_CLOSE(AppFrame::OnClose) EVT_MENU(wxID_ANY, AppFrame::OnMenu) EVT_IDLE(AppFrame::OnIdle) EVT_SPLITTER_DCLICK(wxID_ANY, AppFrame::OnDoubleClickSash) EVT_SPLITTER_UNSPLIT(wxID_ANY, AppFrame::OnUnSplit) wxEND_EVENT_TABLE() #ifdef USE_HAMLIB #include "RigThread.h" #include "PortSelectorDialog.h" #include "rs232.h" #endif class ActionDialogBookmarkReset : public ActionDialog { public: ActionDialogBookmarkReset() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Reset Bookmarks?")) { m_questionText->SetLabelText(wxT("Resetting bookmarks will erase all current bookmarks; are you sure?")); } void doClickOK() { wxGetApp().getBookmarkMgr().resetBookmarks(); wxGetApp().getBookmarkMgr().updateBookmarks(); wxGetApp().getBookmarkMgr().updateActiveList(); } }; /* split a string by 'seperator' into a vector of string */ std::vector str_explode(const std::string &seperator, const std::string &in_str); #define APPFRAME_MODEMPROPS_MINSIZE 20 #define APPFRAME_MODEMPROPS_MAXSIZE 240 AppFrame::AppFrame() : wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(nullptr) { #ifdef __linux__ SetIcon(wxICON(cubicsdr)); #endif wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); demodTray = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodTunerTray = new wxBoxSizer(wxHORIZONTAL); std::vector attribList = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; //wxGLAttributes attribList; //attribList.PlatformDefaults().RGBA().DoubleBuffer().EndList(); //attribList.PlatformDefaults().MinRGBA(8, 8, 8, 8).DoubleBuffer().Depth(16).EndList(); mainSplitter = new wxSplitterWindow( this, wxID_MAIN_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); mainSplitter->SetSashGravity(10.0f / 37.0f); mainSplitter->SetMinimumPaneSize(1); wxPanel *demodPanel = new wxPanel(mainSplitter, wxID_ANY); #ifdef CUBICSDR_HEADER_IMAGE wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath()); std::string headerPath = exePath.GetPath().ToStdString(); headerPath += filePathSeparator + std::string("" CUBICSDR_HEADER_IMAGE); wxInitAllImageHandlers(); ImagePanel *imgPanel = new ImagePanel(demodPanel, headerPath, wxBITMAP_TYPE_ANY); std::string headerBgColor = "" CUBICSDR_HEADER_BG; if (headerBgColor != "") { imgPanel->SetBackgroundColour(wxColour(headerBgColor)); } imgPanel->SetBestFittingSize(wxSize(200, 0)); demodTray->Add(imgPanel, 0, wxEXPAND | wxALL, 0); demodTray->AddSpacer(1); #endif gainCanvas = new GainCanvas(demodPanel, attribList); gainCanvas->setHelpTip("Tuner gains, usually in dB. Click / use Mousewheel to change."); gainSizerItem = demodTray->Add(gainCanvas, 0, wxEXPAND | wxALL, 0); gainSizerItem->Show(false); gainSpacerItem = demodTray->AddSpacer(1); gainSpacerItem->Show(false); std::vector modemList = { "FM", "FMS", "NBFM", "AM", "LSB", "USB", "DSB", "I/Q" }; #ifdef CUBICSDR_MODEM_EXCLUDE std::string excludeListStr = "" CUBICSDR_MODEM_EXCLUDE; std::vector excludeList = str_explode(",",excludeListStr); for (auto ex_i : excludeList) { std::vector::iterator found_i = std::find(modemList.begin(),modemList.end(),ex_i); if (found_i != modemList.end()) { modemList.erase(found_i); } } #endif demodModeSelector = new ModeSelectorCanvas(demodPanel, attribList); for (auto mt_i : modemList) { demodModeSelector->addChoice(mt_i); } #ifdef CUBICSDR_MODEM_EXCLUDE demodModeSelector->setHelpTip("Use buttons to choose modulation type."); #else demodModeSelector->setHelpTip("Choose modulation type: Frequency Modulation (Hotkey F), Amplitude Modulation (A) and Lower (L), Upper (U), Double Side-Band and more."); #endif demodModeSelector->SetMinSize(wxSize(50,-1)); demodModeSelector->SetMaxSize(wxSize(50,-1)); demodTray->Add(demodModeSelector, 2, wxEXPAND | wxALL, 0); #ifdef ENABLE_DIGITAL_LAB demodModeSelectorAdv = new ModeSelectorCanvas(demodPanel, attribList); demodModeSelectorAdv->addChoice("ASK"); demodModeSelectorAdv->addChoice("APSK"); demodModeSelectorAdv->addChoice("BPSK"); demodModeSelectorAdv->addChoice("DPSK"); demodModeSelectorAdv->addChoice("PSK"); demodModeSelectorAdv->addChoice("FSK"); demodModeSelectorAdv->addChoice("GMSK"); demodModeSelectorAdv->addChoice("OOK"); demodModeSelectorAdv->addChoice("ST"); demodModeSelectorAdv->addChoice("SQAM"); demodModeSelectorAdv->addChoice("QAM"); demodModeSelectorAdv->addChoice("QPSK"); demodModeSelectorAdv->setHelpTip("Choose advanced modulation types."); demodModeSelectorAdv->SetMinSize(wxSize(50,-1)); demodModeSelectorAdv->SetMaxSize(wxSize(50,-1)); demodTray->Add(demodModeSelectorAdv, 3, wxEXPAND | wxALL, 0); #endif modemPropertiesUpdated.store(false); modemProps = new ModemProperties(demodPanel, wxID_ANY); modemProps->SetMinSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE,-1)); modemProps->SetMaxSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE,-1)); ModemArgInfoList dummyInfo; modemProps->initProperties(dummyInfo, nullptr); modemProps->updateTheme(); demodTray->Add(modemProps, 15, wxEXPAND | wxALL, 0); #ifndef __APPLE__ demodTray->AddSpacer(1); #endif #if CUBICSDR_ENABLE_VIEW_DEMOD wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); wxGetApp().getDemodSpectrumProcessor()->setup(DEFAULT_DMOD_FFT_SIZE); demodSpectrumCanvas = new SpectrumCanvas(demodPanel, attribList); demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000); demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0); wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue()); demodVisuals->AddSpacer(1); demodWaterfallCanvas = new WaterfallCanvas(demodPanel, attribList); demodWaterfallCanvas->setup(DEFAULT_DMOD_FFT_SIZE, DEFAULT_DEMOD_WATERFALL_LINES_NB); demodWaterfallCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000); demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas); demodWaterfallCanvas->setMinBandwidth(8000); demodSpectrumCanvas->attachWaterfallCanvas(demodWaterfallCanvas); demodVisuals->Add(demodWaterfallCanvas, 6, wxEXPAND | wxALL, 0); wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodWaterfallCanvas->getVisualDataQueue()); demodWaterfallCanvas->getVisualDataQueue()->set_max_num_items(3); demodWaterfallCanvas->setLinesPerSecond((int)(DEFAULT_DEMOD_WATERFALL_LINES_NB / DEMOD_WATERFALL_DURATION_IN_SECONDS)); demodVisuals->SetMinSize(wxSize(128,-1)); demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0); demodTray->AddSpacer(1); #else demodSpectrumCanvas = nullptr; demodWaterfallCanvas = nullptr; #endif demodSignalMeter = new MeterCanvas(demodPanel, attribList); demodSignalMeter->setMax(DEMOD_SIGNAL_MAX); demodSignalMeter->setMin(DEMOD_SIGNAL_MIN); demodSignalMeter->setLevel(DEMOD_SIGNAL_MIN); demodSignalMeter->setInputValue(DEMOD_SIGNAL_MIN); demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level. Right-Click to Auto-Zero Squelch"); demodSignalMeter->SetMinSize(wxSize(12,24)); demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0); demodTray->AddSpacer(1); #if CUBICSDR_ENABLE_VIEW_SCOPE scopeCanvas = new ScopeCanvas(demodPanel, attribList); scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum, 'B' to toggle decibels display."); scopeCanvas->SetMinSize(wxSize(128,-1)); demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0); wxGetApp().getScopeProcessor()->setup(DEFAULT_SCOPE_FFT_SIZE); wxGetApp().getScopeProcessor()->attachOutput(scopeCanvas->getInputQueue()); demodScopeTray->AddSpacer(1); #else scopeCanvas = nullptr; #endif deltaLockButton = new ModeSelectorCanvas(demodPanel, attribList); deltaLockButton->addChoice(1, "V"); deltaLockButton->setPadding(-1,-1); deltaLockButton->setHighlightColor(RGBA4f(0.8f,0.8f,0.2f)); deltaLockButton->setHelpTip("Delta Lock Toggle (V) - Enable to lock modem relative to center frequency."); deltaLockButton->setToggleMode(true); deltaLockButton->setSelection(-1); deltaLockButton->SetMinSize(wxSize(20,28)); demodTunerTray->Add(deltaLockButton, 0, wxEXPAND | wxALL, 0); demodTunerTray->AddSpacer(1); demodTuner = new TuningCanvas(demodPanel, attribList); demodTuner->SetMinClientSize(wxSize(200,28)); demodTunerTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0); demodScopeTray->Add(demodTunerTray, 1, wxEXPAND | wxALL, 0); demodTray->Add(demodScopeTray, 30, wxEXPAND | wxALL, 0); demodTray->AddSpacer(1); wxBoxSizer *demodGainTray = new wxBoxSizer(wxVERTICAL); demodGainMeter = new MeterCanvas(demodPanel, attribList); demodGainMeter->setMax(2.0); demodGainMeter->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level."); demodGainMeter->setShowUserInput(false); demodGainMeter->SetMinSize(wxSize(13,24)); demodGainTray->Add(demodGainMeter, 8, wxEXPAND | wxALL, 0); demodGainTray->AddSpacer(1); soloModeButton = new ModeSelectorCanvas(demodPanel, attribList); soloModeButton->addChoice(1, "S"); soloModeButton->setPadding(-1,-1); soloModeButton->setHighlightColor(RGBA4f(0.8f,0.8f,0.2f)); soloModeButton->setHelpTip("Solo Mode Toggle"); soloModeButton->setToggleMode(true); soloModeButton->setSelection(-1); soloModeButton->SetMinSize(wxSize(12,28)); demodGainTray->Add(soloModeButton, 1, wxEXPAND | wxALL, 0); demodGainTray->AddSpacer(1); demodMuteButton = new ModeSelectorCanvas(demodPanel, attribList); demodMuteButton->addChoice(1, "M"); demodMuteButton->setPadding(-1,-1); demodMuteButton->setHighlightColor(RGBA4f(0.8f,0.2f,0.2f)); demodMuteButton->setHelpTip("Demodulator Mute Toggle"); demodMuteButton->setToggleMode(true); demodMuteButton->setSelection(-1); demodMuteButton->SetMinSize(wxSize(12,28)); demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0); demodTray->Add(demodGainTray, 1, wxEXPAND | wxALL, 0); demodPanel->SetSizer(demodTray); // vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0); // vbox->AddSpacer(1); bookmarkSplitter = new wxSplitterWindow(mainSplitter, wxID_BM_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); bookmarkSplitter->SetMinimumPaneSize(1); bookmarkSplitter->SetSashGravity(1.0f / 20.0f); mainVisSplitter = new wxSplitterWindow( bookmarkSplitter, wxID_VIS_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); mainVisSplitter->SetMinimumPaneSize(1); mainVisSplitter->SetSashGravity(6.0f / 25.0f); // mainVisSplitter->Connect( wxEVT_IDLE, wxIdleEventHandler( AppFrame::mainVisSplitterIdle ), NULL, this ); wxPanel *spectrumPanel = new wxPanel(mainVisSplitter, wxID_ANY); wxBoxSizer *spectrumSizer = new wxBoxSizer(wxHORIZONTAL); wxGetApp().getSpectrumProcessor()->setup(DEFAULT_FFT_SIZE); spectrumCanvas = new SpectrumCanvas(spectrumPanel, attribList); spectrumCanvas->setShowDb(true); spectrumCanvas->setUseDBOfs(true); spectrumCanvas->setScaleFactorEnabled(true); wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue()); wxBoxSizer *spectrumCtlTray = new wxBoxSizer(wxVERTICAL); peakHoldButton = new ModeSelectorCanvas(spectrumPanel, attribList); peakHoldButton->addChoice(1, "P"); peakHoldButton->setPadding(-1,-1); peakHoldButton->setHighlightColor(RGBA4f(0.2f,0.8f,0.2f)); peakHoldButton->setHelpTip("Peak Hold Toggle"); peakHoldButton->setToggleMode(true); peakHoldButton->setSelection(-1); peakHoldButton->SetMinSize(wxSize(12,24)); spectrumCtlTray->Add(peakHoldButton, 1, wxEXPAND | wxALL, 0); spectrumCtlTray->AddSpacer(1); spectrumAvgMeter = new MeterCanvas(spectrumPanel, attribList); spectrumAvgMeter->setHelpTip("Spectrum averaging speed, click or drag to adjust."); spectrumAvgMeter->setMax(1.0); spectrumAvgMeter->setLevel(0.65f); spectrumAvgMeter->setShowUserInput(false); spectrumAvgMeter->SetMinSize(wxSize(12,24)); spectrumCtlTray->Add(spectrumAvgMeter, 8, wxEXPAND | wxALL, 0); spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0); spectrumSizer->AddSpacer(1); spectrumSizer->Add(spectrumCtlTray, 1, wxEXPAND | wxALL, 0); spectrumPanel->SetSizer(spectrumSizer); // vbox->Add(spectrumSizer, 5, wxEXPAND | wxALL, 0); // vbox->AddSpacer(1); wxPanel *waterfallPanel = new wxPanel(mainVisSplitter, wxID_ANY); wxBoxSizer *wfSizer = new wxBoxSizer(wxHORIZONTAL); waterfallCanvas = new WaterfallCanvas(waterfallPanel, attribList); waterfallCanvas->setup(DEFAULT_FFT_SIZE, DEFAULT_MAIN_WATERFALL_LINES_NB); waterfallDataThread = new FFTVisualDataThread(); waterfallDataThread->setInputQueue("IQDataInput", wxGetApp().getWaterfallVisualQueue()); waterfallDataThread->setOutputQueue("FFTDataOutput", waterfallCanvas->getVisualDataQueue()); waterfallDataThread->getProcessor()->setHideDC(true); t_FFTData = new std::thread(&FFTVisualDataThread::threadMain, waterfallDataThread); waterfallSpeedMeter = new MeterCanvas(waterfallPanel, attribList); waterfallSpeedMeter->setHelpTip("Waterfall speed, click or drag to adjust (max 1024 lines per second)"); waterfallSpeedMeter->setMax(sqrt(1024)); waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS)); waterfallSpeedMeter->setShowUserInput(false); waterfallSpeedMeter->SetMinSize(wxSize(12,24)); wfSizer->Add(waterfallCanvas, 63, wxEXPAND | wxALL, 0); wfSizer->AddSpacer(1); wfSizer->Add(waterfallSpeedMeter, 1, wxEXPAND | wxALL, 0); waterfallPanel->SetSizer(wfSizer); // vbox->Add(wfSizer, 20, wxEXPAND | wxALL, 0); mainVisSplitter->SplitHorizontally( spectrumPanel, waterfallPanel, 0 ); bookmarkView = new BookmarkView(bookmarkSplitter, wxID_ANY, wxDefaultPosition, wxSize(120,-1)); bookmarkSplitter->SplitVertically( bookmarkView, mainVisSplitter ); mainSplitter->SplitHorizontally( demodPanel, bookmarkSplitter ); if (!wxGetApp().getConfig()->getBookmarksVisible()) { bookmarkSplitter->Unsplit(bookmarkView); bookmarkSplitter->Layout(); } vbox->Add(mainSplitter, 1, wxEXPAND | wxALL, 0); // TODO: refactor these.. waterfallCanvas->attachSpectrumCanvas(spectrumCanvas); spectrumCanvas->attachWaterfallCanvas(waterfallCanvas); /* * / vbox->AddSpacer(1); testCanvas = new UITestCanvas(this, attribList); vbox->Add(testCanvas, 20, wxEXPAND | wxALL, 0); // */ this->SetSizer(vbox); // SetIcon(wxICON(sample)); // Make a menubar menuBar = new wxMenuBar; menuBar->Append(makeFileMenu(), wxT("&File")); settingsMenu = new wxMenu; menuBar->Append(settingsMenu, wxT("&Settings")); std::vector::iterator devices_i; std::map::iterator mdevices_i; AudioThread::enumerateDevices(devices); int i = 0; for (devices_i = devices.begin(); devices_i != devices.end(); devices_i++) { if (devices_i->inputChannels) { inputDevices[i] = *devices_i; } if (devices_i->outputChannels) { outputDevices[i] = *devices_i; } i++; } wxGetApp().getDemodMgr().setOutputDevices(outputDevices); // // for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { // wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE + mdevices_i->first, mdevices_i->second.name, wxT("Description?")); // itm->SetId(wxID_RT_AUDIO_DEVICE + mdevices_i->first); // if (mdevices_i->second.isDefaultOutput) { // itm->Check(true); // } // outputDeviceMenuItems[mdevices_i->first] = itm; // } // // menuBar->Append(menu, wxT("Audio &Output")); sampleRateMenu = new wxMenu; menuBar->Append(sampleRateMenu, wxT("Sample &Rate")); // Audio Sample Rates wxMenu *audioSampleRateMenu = new wxMenu; #define NUM_RATES_DEFAULT 4 unsigned int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 }; for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { unsigned int desired_rate = 0; unsigned int desired_rank = NUM_RATES_DEFAULT + 1; for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { for (unsigned int i = 0; i < NUM_RATES_DEFAULT; i++) { if (desired_rates[i] == (*srate)) { if (desired_rank > i) { desired_rank = i; desired_rate = (*srate); } } } } if (desired_rank > NUM_RATES_DEFAULT) { desired_rate = mdevices_i->second.sampleRates.back(); } AudioThread::deviceSampleRate[mdevices_i->first] = desired_rate; } for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first; wxMenu *subMenu = new wxMenu; audioSampleRateMenu->AppendSubMenu(subMenu, mdevices_i->second.name, wxT("Description?")); int j = 0; for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { std::stringstream srateName; srateName << ((float) (*srate) / 1000.0f) << "kHz"; wxMenuItem *itm = subMenu->AppendRadioItem(menu_id + j, srateName.str(), wxT("Description?")); if ((int)(*srate) == AudioThread::deviceSampleRate[mdevices_i->first]) { itm->Check(true); } audioSampleRateMenuItems[menu_id + j] = itm; j++; } } menuBar->Append(audioSampleRateMenu, wxT("Audio &Sample Rate")); //Add a Recording menu menuBar->Append(makeRecordingMenu(), wxT("Recordin&g")); // updateRecordingMenu(); //Add Display menu displayMenu = new wxMenu; wxMenu *fontMenu = new wxMenu; int fontScale = wxGetApp().getConfig()->getFontScale(); fontMenu->AppendRadioItem(wxID_DISPLAY_BASE, "Default")->Check(GLFont::GLFONT_SCALE_NORMAL == fontScale); fontMenu->AppendRadioItem(wxID_DISPLAY_BASE + 1, "1.5x")->Check(GLFont::GLFONT_SCALE_MEDIUM == fontScale); fontMenu->AppendRadioItem(wxID_DISPLAY_BASE + 2, "2.0x")->Check(GLFont::GLFONT_SCALE_LARGE == fontScale); displayMenu->AppendSubMenu(fontMenu, "&Text Size"); wxMenu *themeMenu = new wxMenu; int themeId = wxGetApp().getConfig()->getTheme(); themeMenu->AppendRadioItem(wxID_THEME_DEFAULT, "Default")->Check(themeId==COLOR_THEME_DEFAULT); themeMenu->AppendRadioItem(wxID_THEME_RADAR, "RADAR")->Check(themeId==COLOR_THEME_RADAR); themeMenu->AppendRadioItem(wxID_THEME_BW, "Black & White")->Check(themeId==COLOR_THEME_BW); themeMenu->AppendRadioItem(wxID_THEME_SHARP, "Sharp")->Check(themeId==COLOR_THEME_SHARP); themeMenu->AppendRadioItem(wxID_THEME_RAD, "Rad")->Check(themeId==COLOR_THEME_RAD); themeMenu->AppendRadioItem(wxID_THEME_TOUCH, "Touch")->Check(themeId==COLOR_THEME_TOUCH); themeMenu->AppendRadioItem(wxID_THEME_HD, "HD")->Check(themeId==COLOR_THEME_HD); displayMenu->AppendSubMenu(themeMenu, wxT("&Color Scheme")); hideBookmarksItem = displayMenu->AppendCheckItem(wxID_DISPLAY_BOOKMARKS, wxT("Hide Bookmarks")); hideBookmarksItem->Check(!wxGetApp().getConfig()->getBookmarksVisible()); GLFont::setScale((GLFont::GLFontScale)fontScale); #ifdef USE_HAMLIB rigModel = wxGetApp().getConfig()->getRigModel(); rigSerialRate = wxGetApp().getConfig()->getRigRate(); rigPort = wxGetApp().getConfig()->getRigPort(); rigPortDialog = nullptr; rigMenu = new wxMenu; rigEnableMenuItem = rigMenu->AppendCheckItem(wxID_RIG_TOGGLE, wxT("Enable Rig")); rigMenu->Append(wxID_RIG_SDR_IF, wxT("SDR-IF")); rigControlMenuItem = rigMenu->AppendCheckItem(wxID_RIG_CONTROL, wxT("Control Rig")); rigControlMenuItem->Check(wxGetApp().getConfig()->getRigControlMode()); rigFollowMenuItem = rigMenu->AppendCheckItem(wxID_RIG_FOLLOW, wxT("Follow Rig")); rigFollowMenuItem->Check(wxGetApp().getConfig()->getRigFollowMode()); rigCenterLockMenuItem = rigMenu->AppendCheckItem(wxID_RIG_CENTERLOCK, wxT("Floating Center")); rigCenterLockMenuItem->Check(wxGetApp().getConfig()->getRigCenterLock()); rigFollowModemMenuItem = rigMenu->AppendCheckItem(wxID_RIG_FOLLOW_MODEM, wxT("Track Modem")); rigFollowModemMenuItem->Check(wxGetApp().getConfig()->getRigFollowModem()); wxMenu *rigModelMenu = new wxMenu; RigList &rl = RigThread::enumerate(); numRigs = rl.size(); int modelMenuId = wxID_RIG_MODEL_BASE; for (RigList::const_iterator ri = rl.begin(); ri != rl.end(); ri++) { std::string modelString((*ri)->mfg_name); modelString.append(" "); modelString.append((*ri)->model_name); rigModelMenuItems[(*ri)->rig_model] = rigModelMenu->AppendRadioItem(modelMenuId, modelString, wxT("Description?")); if (rigModel == (*ri)->rig_model) { rigModelMenuItems[(*ri)->rig_model]->Check(true); } modelMenuId++; } rigMenu->AppendSubMenu(rigModelMenu, wxT("Model")); wxMenu *rigSerialMenu = new wxMenu; rigSerialRates.push_back(1200); rigSerialRates.push_back(2400); rigSerialRates.push_back(4800); rigSerialRates.push_back(9600); rigSerialRates.push_back(19200); rigSerialRates.push_back(38400); rigSerialRates.push_back(57600); rigSerialRates.push_back(115200); rigSerialRates.push_back(128000); rigSerialRates.push_back(256000); int rateMenuId = wxID_RIG_SERIAL_BASE; for (std::vector::const_iterator rate_i = rigSerialRates.begin(); rate_i != rigSerialRates.end(); rate_i++) { std::string rateString; rateString.append(std::to_string((*rate_i))); rateString.append(" baud"); rigSerialMenuItems[(*rate_i)] = rigSerialMenu->AppendRadioItem(rateMenuId, rateString, wxT("Description?")); if (rigSerialRate == (*rate_i)) { rigSerialMenuItems[(*rate_i)]->Check(true); } rateMenuId++; } rigMenu->AppendSubMenu(rigSerialMenu, wxT("Serial Rate")); rigPortMenuItem = rigMenu->Append(wxID_RIG_PORT, wxT("Control Port")); menuBar->Append(rigMenu, wxT("&Rig Control")); #endif menuBar->Append(displayMenu, wxT("&Display")); SetMenuBar(menuBar); CreateStatusBar(); wxRect *win = wxGetApp().getConfig()->getWindow(); if (win) { this->SetPosition(win->GetPosition()); this->SetClientSize(win->GetSize()); } else { SetClientSize(1280, 600); Centre(); } bool max = wxGetApp().getConfig()->getWindowMaximized(); if (max) { this->Maximize(); } long long freqSnap = wxGetApp().getConfig()->getSnap(); wxGetApp().setFrequencySnap(freqSnap); float spectrumAvg = wxGetApp().getConfig()->getSpectrumAvgSpeed(); spectrumAvgMeter->setLevel(spectrumAvg); wxGetApp().getSpectrumProcessor()->setFFTAverageRate(spectrumAvg); int wflps =wxGetApp().getConfig()->getWaterfallLinesPerSec(); waterfallSpeedMeter->setLevel(sqrt(wflps)); waterfallDataThread->setLinesPerSecond(wflps); waterfallCanvas->setLinesPerSecond(wflps); ThemeMgr::mgr.setTheme(wxGetApp().getConfig()->getTheme()); bookmarkView->updateTheme(); int mpc =wxGetApp().getConfig()->getModemPropsCollapsed(); if (mpc) { modemProps->setCollapsed(true); } int msPos = wxGetApp().getConfig()->getMainSplit(); if (msPos != -1) { mainSplitter->SetSashPosition(msPos); } int bsPos = wxGetApp().getConfig()->getBookmarkSplit(); if (bsPos != -1) { bookmarkSplitter->SetSashPosition(bsPos); } int vsPos = wxGetApp().getConfig()->getVisSplit(); if (vsPos != -1) { mainVisSplitter->SetSashPosition(vsPos); } Show(); #ifdef _WIN32 SetIcon(wxICON(frame_icon)); #endif wxAcceleratorEntry entries[3]; entries[0].Set(wxACCEL_CTRL, (int) 'O', wxID_OPEN); entries[1].Set(wxACCEL_CTRL, (int) 'S', wxID_SAVE); entries[2].Set(wxACCEL_CTRL, (int) 'A', wxID_SAVEAS); wxAcceleratorTable accel(3, entries); SetAcceleratorTable(accel); deviceChanged.store(false); devInfo = NULL; wxGetApp().deviceSelector(); saveDisabled = false; aboutDlg = nullptr; // static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; // wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not"); // ShowFullScreen(true); //Force refresh of all Refresh(); } AppFrame::~AppFrame() { waterfallDataThread->terminate(); t_FFTData->join(); } wxMenu *AppFrame::makeFileMenu() { wxMenu *menu = new wxMenu; #ifndef __APPLE__ #ifdef CUBICSDR_ENABLE_ABOUT_DIALOG menu->Append(wxID_ABOUT_CUBICSDR, "About " CUBICSDR_INSTALL_NAME); #endif #endif menu->Append(wxID_SDR_DEVICES, "SDR Devices"); menu->AppendSeparator(); menu->Append(wxID_SDR_START_STOP, "Stop / Start Device"); menu->AppendSeparator(); wxMenu *sessionMenu = new wxMenu; sessionMenu->Append(wxID_OPEN, "&Open Session"); sessionMenu->Append(wxID_SAVE, "&Save Session"); sessionMenu->Append(wxID_SAVEAS, "Save Session &As.."); sessionMenu->AppendSeparator(); sessionMenu->Append(wxID_RESET, "&Reset Session"); menu->AppendSubMenu(sessionMenu, "Session"); menu->AppendSeparator(); wxMenu *bookmarkMenu = new wxMenu; bookmarkMenu->Append(wxID_OPEN_BOOKMARKS, "Open Bookmarks"); bookmarkMenu->Append(wxID_SAVE_BOOKMARKS, "Save Bookmarks"); bookmarkMenu->Append(wxID_SAVEAS_BOOKMARKS, "Save Bookmarks As.."); bookmarkMenu->AppendSeparator(); bookmarkMenu->Append(wxID_RESET_BOOKMARKS, "Reset Bookmarks"); menu->AppendSubMenu(bookmarkMenu, "Bookmarks"); #ifndef __APPLE__ menu->AppendSeparator(); menu->Append(wxID_CLOSE); #else #ifdef CUBICSDR_ENABLE_ABOUT_DIALOG if (wxApp::s_macAboutMenuItemId != wxID_NONE) { wxString aboutLabel; aboutLabel.Printf(_("About %s"), CUBICSDR_INSTALL_NAME); menu->Append(wxApp::s_macAboutMenuItemId, aboutLabel); } #endif #endif fileMenu = menu; return menu; } wxMenu *AppFrame::makeRecordingMenu() { recordingMenuItems.clear(); wxMenu *menu = new wxMenu; recordingMenuItems[wxID_RECORDING_PATH] = menu->Append(wxID_RECORDING_PATH, getSettingsLabel("Set Recording Path", "")); menu->AppendSeparator(); //Squelch options as sub-menu: wxMenu *subMenu = new wxMenu; recordingMenuItems[wxID_RECORDING_SQUELCH_BASE] = menu->AppendSubMenu(subMenu, "Squelch"); recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_SILENCE, "Record Silence", "Record below squelch-break audio as silence, i.e records as the user may hear."); recordingMenuItems[wxID_RECORDING_SQUELCH_SKIP] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_SKIP, "Skip Silence", "Do not record below squelch-break audio, i.e squelch-break audio parts are packed together."); recordingMenuItems[wxID_RECORDING_SQUELCH_ALWAYS] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_ALWAYS, "Record Always", "Record everything irrespective of the squelch level."); recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT] = menu->Append(wxID_RECORDING_FILE_TIME_LIMIT, getSettingsLabel("File time limit", ""), "Creates a new file automatically, each time the recording lasts longer than the limit, named according to the current time."); recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true); recordingMenu = menu; return menu; } void AppFrame::updateRecordingMenu() { // Recording path: std::string recPath = wxGetApp().getConfig()->getRecordingPath(); if (recPath.length() > 32) { recPath = "..." + recPath.substr(recPath.length() - 32, 32); } recordingMenuItems[wxID_RECORDING_PATH]->SetItemLabel(getSettingsLabel("Set Recording Path", recPath.empty() ? "" : recPath)); //Squelch options: int squelchEnumValue = wxGetApp().getConfig()->getRecordingSquelchOption(); if (squelchEnumValue == AudioSinkFileThread::SQUELCH_RECORD_SILENCE) { recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true); recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Silence")); } else if (squelchEnumValue == AudioSinkFileThread::SQUELCH_SKIP_SILENCE) { recordingMenuItems[wxID_RECORDING_SQUELCH_SKIP]->Check(true); recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Skip Silence")); } else if (squelchEnumValue == AudioSinkFileThread::SQUELCH_RECORD_ALWAYS) { recordingMenuItems[wxID_RECORDING_SQUELCH_ALWAYS]->Check(true); recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Always")); } else { recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true); recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Silence")); } //File time limit: int fileTimeLimitSeconds = wxGetApp().getConfig()->getRecordingFileTimeLimit(); if (fileTimeLimitSeconds <= 0) { recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT]->SetItemLabel(getSettingsLabel("File time limit","")); } else { recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT]->SetItemLabel(getSettingsLabel("File time limit", std::to_string(fileTimeLimitSeconds), "s")); } } void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) { this->devInfo = devInfo; deviceChanged.store(true); } void AppFrame::notifyDeviceChanged() { deviceChanged.store(true); } void AppFrame::updateDeviceParams() { if (!deviceChanged.load() || devInfo == nullptr) { return; } int i = 0; SoapySDR::Device *soapyDev = devInfo->getSoapyDevice(); // Build settings menu wxMenu *newSettingsMenu = new wxMenu; showTipMenuItem = newSettingsMenu->AppendCheckItem(wxID_SET_TIPS, "Show Hover Tips"); showTipMenuItem->Check(wxGetApp().getConfig()->getShowTips()); lowPerfMode = wxGetApp().getConfig()->getLowPerfMode(); lowPerfMenuItem = newSettingsMenu->AppendCheckItem(wxID_LOW_PERF, "Reduce CPU Usage"); if (lowPerfMode) { lowPerfMenuItem->Check(true); } newSettingsMenu->AppendSeparator(); settingsMenuItems.clear(); settingsMenuItems[wxID_SET_DB_OFFSET] = newSettingsMenu->Append(wxID_SET_DB_OFFSET, getSettingsLabel("Power Level Offset", std::to_string(wxGetApp().getConfig()->getDBOffset()), "dB")); settingsMenuItems[wxID_SET_FREQ_OFFSET] = newSettingsMenu->Append(wxID_SET_FREQ_OFFSET, getSettingsLabel("Frequency Offset", std::to_string(wxGetApp().getOffset() / 1000 ) , "KHz")); if (devInfo->hasCORR(SOAPY_SDR_RX, 0)) { settingsMenuItems[wxID_SET_PPM] = newSettingsMenu->Append(wxID_SET_PPM, getSettingsLabel("Device PPM", std::to_string(wxGetApp().getPPM()) , "ppm")); } if (devInfo->getDriver() != "rtlsdr") { iqSwapMenuItem = newSettingsMenu->AppendCheckItem(wxID_SET_IQSWAP, "I/Q Swap"); iqSwapMenuItem->Check(wxGetApp().getSDRThread()->getIQSwap()); } agcMenuItem = nullptr; if (soapyDev->listGains(SOAPY_SDR_RX, 0).size()) { agcMenuItem = newSettingsMenu->AppendCheckItem(wxID_AGC_CONTROL, "Automatic Gain"); agcMenuItem->Check(wxGetApp().getAGCMode()); } else if (!wxGetApp().getAGCMode()) { wxGetApp().setAGCMode(true); } //Add an Antenna menu if more than one (RX) antenna, to keep the UI free of useless entries antennaNames.clear(); antennaMenuItems.clear(); std::vector availableAntennas = devInfo->getAntennaNames(SOAPY_SDR_RX, 0); if (availableAntennas.size() > 1) { newSettingsMenu->AppendSeparator(); antennaNames = availableAntennas; wxMenu *subMenu = new wxMenu; int i = 0; std::string antennaChecked; for (std::string currentAntenna : availableAntennas) { antennaMenuItems[wxID_ANTENNAS_BASE + i] = subMenu->AppendRadioItem(wxID_ANTENNAS_BASE + i, currentAntenna); if (wxGetApp().getAntennaName() == currentAntenna) { antennaMenuItems[wxID_ANTENNAS_BASE + i]->Check(true); antennaChecked = currentAntenna; } i++; } antennaMenuItems[wxID_ANTENNA_CURRENT] = newSettingsMenu->AppendSubMenu(subMenu, "Antenna"); //Change the Antenna label to indicate the current antenna. if (!antennaChecked.empty()) { antennaMenuItems[wxID_ANTENNA_CURRENT]->SetItemLabel(getSettingsLabel("Antenna", antennaChecked)); } } //Add an informative, read-only menu entry to display the current TX selected antenna, if any. if (devInfo->getAntennaNames(SOAPY_SDR_TX, 0).size() > 1) { currentTXantennaName = devInfo->getAntennaName(SOAPY_SDR_TX, 0); newSettingsMenu->AppendSeparator(); antennaMenuItems[wxID_ANTENNA_CURRENT_TX] = newSettingsMenu->Append(wxID_ANTENNA_CURRENT_TX, getSettingsLabel("TX Antenna", currentTXantennaName)); antennaMenuItems[wxID_ANTENNA_CURRENT_TX]->Enable(false); } //Runtime settings part SoapySDR::ArgInfoList::const_iterator args_i; settingArgs = soapyDev->getSettingInfo(); if (settingArgs.size()) { newSettingsMenu->AppendSeparator(); } //for each Runtime option of index i: for (args_i = settingArgs.begin(); args_i != settingArgs.end(); args_i++) { SoapySDR::ArgInfo arg = (*args_i); std::string currentVal = soapyDev->readSetting(arg.key); if (arg.type == SoapySDR::ArgInfo::BOOL) { wxMenuItem *item = newSettingsMenu->AppendCheckItem(wxID_SETTINGS_BASE+i, arg.name, arg.description); item->Check(currentVal=="true"); i++; } else if (arg.type == SoapySDR::ArgInfo::INT) { settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description); i++; } else if (arg.type == SoapySDR::ArgInfo::FLOAT) { settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description); i++; } else if (arg.type == SoapySDR::ArgInfo::STRING) { if (arg.options.size()) { wxMenu *subMenu = new wxMenu; int j = 0; std::vector subItemsIds; //for each of this options for (std::string optName : arg.options) { //by default the option name is the same as the displayed name. std::string displayName = optName; if (arg.optionNames.size()) { displayName = arg.optionNames[j]; } wxMenuItem *item = subMenu->AppendRadioItem(wxID_SETTINGS_BASE+i, displayName); subItemsIds.push_back(wxID_SETTINGS_BASE + i); if (currentVal == optName) { item->Check(true); } j++; i++; } settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->AppendSubMenu(subMenu, getSettingsLabel(arg.name, currentVal, arg.units), arg.description); //map subitems ids to their parent item ! for (int currentSubId : subItemsIds) { settingsMenuItems[currentSubId] = settingsMenuItems[wxID_SETTINGS_BASE + i]; } } else { settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description); i++; } } } settingsIdMax = wxID_SETTINGS_BASE+i; menuBar->Replace(1, newSettingsMenu, wxT("&Settings")); settingsMenu = newSettingsMenu; // Build/Rebuild the sample rate menu : sampleRates = devInfo->getSampleRates(SOAPY_SDR_RX, 0); sampleRateMenuItems.clear(); wxMenu *newSampleRateMenu = new wxMenu; int ofs = 0; //Current sample rate, try to keep it as is. long sampleRate = wxGetApp().getSampleRate(); long minRate = sampleRates.front(); long maxRate = sampleRates.back(); //If it is beyond limits, make device choose a reasonable value if (sampleRate < minRate || sampleRate > maxRate) { sampleRate = devInfo->getSampleRateNear(SOAPY_SDR_RX, 0, sampleRate); } //Check if a manual entry was previously set: if so, check its value is still within the limits of the device. If not so, reset it. if (manualSampleRate > 0 && (manualSampleRate < minRate || manualSampleRate > maxRate)) { manualSampleRate = -1; } bool checked = false; for (vector::iterator i = sampleRates.begin(); i != sampleRates.end(); i++) { sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_BASE+ofs, frequencyToStr(*i)); if (sampleRate == (*i)) { sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs]->Check(true); checked = true; } ofs++; } //Add a manual sample value radio button, but disabled by default in case the user //never ever uses manual entry. if (manualSampleRate <= 0) { sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual : N/A")); sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(false); } else { sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual : ") + frequencyToStr(manualSampleRate)); sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true); } //We apply the current sample rate after all if (!checked) { sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true); } //Append a normal button (NOT a radio-button) for manual entry dialog at the end newSampleRateMenu->AppendSeparator(); sampleRateMenuItems[wxID_BANDWIDTH_MANUAL_DIALOG] = newSampleRateMenu->Append(wxID_BANDWIDTH_MANUAL_DIALOG, wxT("Manual Entry...")); menuBar->Replace(2, newSampleRateMenu, wxT("Sample &Rate")); sampleRateMenu = newSampleRateMenu; if (!wxGetApp().getAGCMode()) { gainSpacerItem->Show(true); gainSizerItem->Show(true); gainSizerItem->SetMinSize(devInfo->getSoapyDevice()->listGains(SOAPY_SDR_RX,0).size()*50,0); demodTray->Layout(); } else { gainSpacerItem->Show(false); gainSizerItem->Show(false); demodTray->Layout(); } #if USE_HAMLIB if (wxGetApp().getConfig()->getRigEnabled() && !wxGetApp().rigIsActive()) { enableRig(); rigEnableMenuItem->Check(true); } std::string deviceId = devInfo->getDeviceId(); DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); if (wxGetApp().rigIsActive()) { rigSDRIF = devConfig->getRigIF(rigModel); if (rigSDRIF) { wxGetApp().lockFrequency(rigSDRIF); } else { wxGetApp().unlockFrequency(); } } #endif deviceChanged.store(false); } #ifdef USE_HAMLIB void AppFrame::enableRig() { wxGetApp().stopRig(); wxGetApp().initRig(rigModel, rigPort, rigSerialRate); if (devInfo != nullptr) { std::string deviceId = devInfo->getDeviceId(); DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); rigSDRIF = devConfig->getRigIF(rigModel); if (rigSDRIF) { wxGetApp().lockFrequency(rigSDRIF); } else { wxGetApp().unlockFrequency(); } } else { wxGetApp().unlockFrequency(); } wxGetApp().getConfig()->setRigEnabled(true); } void AppFrame::disableRig() { wxGetApp().stopRig(); wxGetApp().unlockFrequency(); wxGetApp().getConfig()->setRigEnabled(false); } void AppFrame::setRigControlPort(std::string portName) { if (rigPortDialog == nullptr) { return; } if (portName != "") { rigPort = portName; wxGetApp().stopRig(); wxGetApp().initRig(rigModel, rigPort, rigSerialRate); Refresh(); } rigPortDialog->EndModal(0); delete rigPortDialog; rigPortDialog = nullptr; } void AppFrame::dismissRigControlPortDialog() { rigPortDialog->EndModal(0); delete rigPortDialog; rigPortDialog = nullptr; } #endif bool AppFrame::actionOnMenuDisplay(wxCommandEvent& event) { //by default, is managed. bool bManaged = true; if (event.GetId() == wxID_THEME_DEFAULT) { ThemeMgr::mgr.setTheme(COLOR_THEME_DEFAULT); } else if (event.GetId() == wxID_THEME_SHARP) { ThemeMgr::mgr.setTheme(COLOR_THEME_SHARP); } else if (event.GetId() == wxID_THEME_BW) { ThemeMgr::mgr.setTheme(COLOR_THEME_BW); } else if (event.GetId() == wxID_THEME_RAD) { ThemeMgr::mgr.setTheme(COLOR_THEME_RAD); } else if (event.GetId() == wxID_THEME_TOUCH) { ThemeMgr::mgr.setTheme(COLOR_THEME_TOUCH); } else if (event.GetId() == wxID_THEME_HD) { ThemeMgr::mgr.setTheme(COLOR_THEME_HD); } else if (event.GetId() == wxID_THEME_RADAR) { ThemeMgr::mgr.setTheme(COLOR_THEME_RADAR); } //Display : font sizes else if (event.GetId() == wxID_DISPLAY_BASE) { GLFont::setScale(GLFont::GLFONT_SCALE_NORMAL); } else if (event.GetId() == wxID_DISPLAY_BASE + 1) { GLFont::setScale(GLFont::GLFONT_SCALE_MEDIUM); } else if (event.GetId() == wxID_DISPLAY_BASE + 2) { GLFont::setScale(GLFont::GLFONT_SCALE_LARGE); } else if (event.GetId() == wxID_DISPLAY_BOOKMARKS) { if (hideBookmarksItem->IsChecked()) { bookmarkSplitter->Unsplit(bookmarkView); bookmarkSplitter->Layout(); } else { bookmarkSplitter->SplitVertically(bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit()); bookmarkSplitter->Layout(); } } else { bManaged = false; } //update theme choice in children elements: if (event.GetId() >= wxID_THEME_DEFAULT && event.GetId() <= wxID_THEME_RADAR) { gainCanvas->setThemeColors(); modemProps->updateTheme(); bookmarkView->updateTheme(); } //force all windows refresh if (bManaged) { Refresh(); } return bManaged; } bool AppFrame::actionOnMenuReset(wxCommandEvent& event) { if (event.GetId() == wxID_RESET) { wxGetApp().getDemodMgr().terminateAll(); wxGetApp().setFrequency(100000000); wxGetApp().getDemodMgr().setLastDemodulatorType("FM"); demodModeSelector->setSelection(1); wxGetApp().getDemodMgr().setLastMuted(false); wxGetApp().getDemodMgr().setLastBandwidth(DEFAULT_DEMOD_BW); wxGetApp().getDemodMgr().setLastGain(1.0); wxGetApp().getDemodMgr().setLastSquelchLevel(-100); waterfallCanvas->setBandwidth(wxGetApp().getSampleRate()); waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); spectrumCanvas->setBandwidth(wxGetApp().getSampleRate()); spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); waterfallDataThread->setLinesPerSecond(DEFAULT_WATERFALL_LPS); waterfallCanvas->setLinesPerSecond(DEFAULT_WATERFALL_LPS); waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS)); wxGetApp().getSpectrumProcessor()->setFFTAverageRate(0.65f); spectrumAvgMeter->setLevel(0.65f); SetTitle(CUBICSDR_TITLE); currentSessionFile = ""; currentBookmarkFile = ""; bookmarkSplitter->Unsplit(bookmarkView); bookmarkSplitter->SplitVertically(bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit()); hideBookmarksItem->Check(false); //force all windows refresh Refresh(); return true; } return false; } bool AppFrame::actionOnMenuAbout(wxCommandEvent& event) { #ifdef __APPLE__ if (event.GetId() == wxApp::s_macAboutMenuItemId) { #else if (event.GetId() == wxID_ABOUT_CUBICSDR) { #endif if (aboutDlg != nullptr) { aboutDlg->Raise(); aboutDlg->SetFocus(); } else { aboutDlg = new AboutDialog(NULL); aboutDlg->Connect(wxEVT_CLOSE_WINDOW, wxCommandEventHandler(AppFrame::OnAboutDialogClose), NULL, this); aboutDlg->Show(); } return true; } return false; } bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) { if (event.GetId() >= wxID_ANTENNAS_BASE && event.GetId() < wxID_ANTENNAS_BASE + antennaNames.size()) { wxGetApp().setAntennaName(antennaNames[event.GetId() - wxID_ANTENNAS_BASE]); antennaMenuItems[wxID_ANTENNA_CURRENT]->SetItemLabel(getSettingsLabel("Antenna", wxGetApp().getAntennaName())); return true; } else if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) { int setIdx = event.GetId() - wxID_SETTINGS_BASE; int menuIdx = 0; for (std::vector::iterator arg_i = settingArgs.begin(); arg_i != settingArgs.end(); arg_i++) { SoapySDR::ArgInfo &arg = (*arg_i); if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size() && setIdx >= menuIdx && setIdx < menuIdx + (int)arg.options.size()) { int optIdx = setIdx - menuIdx; wxGetApp().getSDRThread()->writeSetting(arg.key, arg.options[optIdx]); //update parent menu item label to display the current value settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, arg.options[optIdx], arg.units)); break; } else if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) { menuIdx += arg.options.size(); } else if (menuIdx == setIdx) { if (arg.type == SoapySDR::ArgInfo::BOOL) { wxGetApp().getSDRThread()->writeSetting(arg.key, (wxGetApp().getSDRThread()->readSetting(arg.key) == "true") ? "false" : "true"); break; } else if (arg.type == SoapySDR::ArgInfo::STRING) { wxString stringVal = wxGetTextFromUser(arg.description, arg.name, wxGetApp().getSDRThread()->readSetting(arg.key)); settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, stringVal.ToStdString(), arg.units)); if (stringVal.ToStdString() != "") { wxGetApp().getSDRThread()->writeSetting(arg.key, stringVal.ToStdString()); } break; } else if (arg.type == SoapySDR::ArgInfo::INT) { int currentVal; try { currentVal = std::stoi(wxGetApp().getSDRThread()->readSetting(arg.key)); } catch (std::invalid_argument e) { currentVal = 0; } int intVal = wxGetNumberFromUser(arg.description, arg.units, arg.name, currentVal, arg.range.minimum(), arg.range.maximum(), this); settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, std::to_string(intVal), arg.units)); if (intVal != -1) { wxGetApp().getSDRThread()->writeSetting(arg.key, std::to_string(intVal)); } break; } else if (arg.type == SoapySDR::ArgInfo::FLOAT) { wxString floatVal = wxGetTextFromUser(arg.description, arg.name, wxGetApp().getSDRThread()->readSetting(arg.key)); try { wxGetApp().getSDRThread()->writeSetting(arg.key, floatVal.ToStdString()); } catch (std::invalid_argument e) { // ... } settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, floatVal.ToStdString(), arg.units)); break; } else { menuIdx++; } } else { menuIdx++; } } //end for return true; } return false; } bool AppFrame::actionOnMenuAGC(wxCommandEvent& event) { if (event.GetId() == wxID_AGC_CONTROL) { if (wxGetApp().getDevice() == NULL) { agcMenuItem->Check(true); return true; } if (!wxGetApp().getAGCMode()) { wxGetApp().setAGCMode(true); gainSpacerItem->Show(false); gainSizerItem->Show(false); demodTray->Layout(); } else { wxGetApp().setAGCMode(false); gainSpacerItem->Show(true); gainSizerItem->Show(true); gainSizerItem->SetMinSize(wxGetApp().getDevice()->getSoapyDevice()->listGains(SOAPY_SDR_RX, 0).size() * 40, 0); demodTray->Layout(); gainCanvas->updateGainUI(); } //full Refresh, some graphical elements has changed Refresh(); return true; } return false; } bool AppFrame::actionOnMenuSampleRate(wxCommandEvent& event) { if (event.GetId() == wxID_BANDWIDTH_MANUAL) { wxGetApp().setSampleRate(manualSampleRate); return true; } else if (event.GetId() == wxID_BANDWIDTH_MANUAL_DIALOG) { int rateHigh = 0, rateLow = 0; SDRDeviceInfo *dev = wxGetApp().getDevice(); if (dev != nullptr) { std::vector sampleRates = dev->getSampleRates(SOAPY_SDR_RX, 0); //default rateLow = MANUAL_SAMPLE_RATE_MIN; rateHigh = MANUAL_SAMPLE_RATE_MAX; if (sampleRates.size()) { rateLow = sampleRates.front(); rateHigh = sampleRates.back(); } long bw = wxGetNumberFromUser("\n" + dev->getName() + "\n\n " + "min: " + std::to_string(rateLow) + " Hz" + ", max: " + std::to_string(rateHigh) + " Hz\n", "Sample Rate in Hz", "Manual Sample Rate Entry", //If a manual sample rate has already been input, recall this one. manualSampleRate > 0 ? manualSampleRate : wxGetApp().getSampleRate(), rateLow, rateHigh, this); if (bw != -1) { manualSampleRate = bw; sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true); sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->SetItemLabel(wxT("Manual : ") + frequencyToStr(manualSampleRate)); sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true); wxGetApp().setSampleRate(manualSampleRate); } } return true; } else if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE + (int)sampleRates.size()) { wxGetApp().setSampleRate(sampleRates[event.GetId() - wxID_BANDWIDTH_BASE]); return true; } return false; } bool AppFrame::actionOnMenuAudioSampleRate(wxCommandEvent& event) { if (event.GetId() >= wxID_AUDIO_BANDWIDTH_BASE) { int evId = event.GetId(); std::vector::iterator devices_i; std::map::iterator mdevices_i; int i = 0; for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first; int j = 0; for (std::vector::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end(); srate++) { if (evId == menu_id + j) { //audioSampleRateMenuItems[menu_id+j]; //std::cout << "Would set audio sample rate on device " << mdevices_i->second.name << " (" << mdevices_i->first << ") to " << (*srate) << "Hz" << std::endl; AudioThread::setDeviceSampleRate(mdevices_i->first, *srate); return true; } j++; } i++; } } return false; } bool AppFrame::actionOnMenuLoadSave(wxCommandEvent& event) { if (event.GetId() == wxID_SAVE) { if (!currentSessionFile.empty()) { saveSession(currentSessionFile); } else { wxFileDialog saveFileDialog(this, _("Save XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (saveFileDialog.ShowModal() == wxID_CANCEL) { return true; } saveSession(saveFileDialog.GetPath().ToStdString()); } return true; } else if (event.GetId() == wxID_OPEN) { wxFileDialog openFileDialog(this, _("Open XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (openFileDialog.ShowModal() == wxID_CANCEL) { return true; } loadSession(openFileDialog.GetPath().ToStdString()); return true; } else if (event.GetId() == wxID_SAVEAS) { wxFileDialog saveFileDialog(this, _("Save XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (saveFileDialog.ShowModal() == wxID_CANCEL) { return true; } saveSession(saveFileDialog.GetPath().ToStdString()); return true; } //save mecanic for bookmark files else if (event.GetId() == wxID_SAVE_BOOKMARKS) { if (!currentBookmarkFile.empty()) { wxGetApp().getBookmarkMgr().saveToFile(currentBookmarkFile, false, true); } else { wxFileDialog saveFileDialog(this, _("Save XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (saveFileDialog.ShowModal() == wxID_CANCEL) { return true; } // Make sure the file name actually ends in .xml std::string fileName = saveFileDialog.GetPath().ToStdString(); std::string lcFileName = fileName; std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower); if (lcFileName.find_last_of(".xml") != lcFileName.length() - 1) { fileName.append(".xml"); } wxGetApp().getBookmarkMgr().saveToFile(fileName, false, true); currentBookmarkFile = fileName; } return true; } else if (event.GetId() == wxID_OPEN_BOOKMARKS) { wxFileDialog openFileDialog(this, _("Open XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (openFileDialog.ShowModal() == wxID_CANCEL) { return true; } if (wxGetApp().getBookmarkMgr().loadFromFile(openFileDialog.GetPath().ToStdString(), false, true)) { wxGetApp().getBookmarkMgr().updateBookmarks(); wxGetApp().getBookmarkMgr().updateActiveList(); currentBookmarkFile = openFileDialog.GetPath().ToStdString(); } else { //failure at loading. currentBookmarkFile = ""; } return true; } else if (event.GetId() == wxID_SAVEAS_BOOKMARKS) { wxFileDialog saveFileDialog(this, _("Save XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (saveFileDialog.ShowModal() == wxID_CANCEL) { return true; } // Make sure the file name actually ends in .xml std::string fileName = saveFileDialog.GetPath().ToStdString(); std::string lcFileName = fileName; std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower); if (lcFileName.find_last_of(".xml") != lcFileName.length() - 1) { fileName.append(".xml"); } wxGetApp().getBookmarkMgr().saveToFile(fileName, false, true); currentBookmarkFile = fileName; return true; } else if (event.GetId() == wxID_RESET_BOOKMARKS) { ActionDialog::showDialog(new ActionDialogBookmarkReset()); return true; } return false; } bool AppFrame::actionOnMenuRecording(wxCommandEvent& event) { if (event.GetId() == wxID_RECORDING_PATH) { std::string recPath = wxGetApp().getConfig()->getRecordingPath(); wxDirDialog recPathDialog(this, _("File Path for Recordings"), recPath, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); if (recPathDialog.ShowModal() == wxID_CANCEL) { return true; } wxGetApp().getConfig()->setRecordingPath(recPathDialog.GetPath().ToStdString()); updateRecordingMenu(); return true; } else if (event.GetId() == wxID_RECORDING_SQUELCH_SILENCE) { wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_RECORD_SILENCE); updateRecordingMenu(); return true; } else if (event.GetId() == wxID_RECORDING_SQUELCH_SKIP) { wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_SKIP_SILENCE); updateRecordingMenu(); return true; } else if (event.GetId() == wxID_RECORDING_SQUELCH_ALWAYS) { wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_RECORD_ALWAYS); updateRecordingMenu(); return true; } else if (event.GetId() == wxID_RECORDING_FILE_TIME_LIMIT) { int currentFileLimitSeconds = wxGetApp().getConfig()->getRecordingFileTimeLimit(); long newFileLimit = wxGetNumberFromUser(wxString("\nFile time limit:\n") + "\nCreates a new file automatically, each time the recording lasts longer than the limit, named according to the current time.\n\n " + + "min: 0 s (no limit)" + ", max: 36000 s (10 hours)\n", "Time in seconds", "File Time Limit", //If a manual sample rate has already been input, recall this one. currentFileLimitSeconds > 0 ? currentFileLimitSeconds : 0, 0, 36000, this); if (newFileLimit != -1) { wxGetApp().getConfig()->setRecordingFileTimeLimit((int)newFileLimit); updateRecordingMenu(); } return true; } return false; } bool AppFrame::actionOnMenuRig(wxCommandEvent& event) { bool bManaged = false; #ifdef USE_HAMLIB bool resetRig = false; if (event.GetId() >= wxID_RIG_MODEL_BASE && event.GetId() < wxID_RIG_MODEL_BASE + numRigs) { int rigIdx = event.GetId() - wxID_RIG_MODEL_BASE; RigList &rl = RigThread::enumerate(); rigModel = rl[rigIdx]->rig_model; if (devInfo != nullptr) { std::string deviceId = devInfo->getDeviceId(); DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); rigSDRIF = devConfig->getRigIF(rigModel); if (rigSDRIF) { wxGetApp().lockFrequency(rigSDRIF); } else { wxGetApp().unlockFrequency(); } } else { wxGetApp().unlockFrequency(); } resetRig = true; bManaged = true; } if (event.GetId() >= wxID_RIG_SERIAL_BASE && event.GetId() < wxID_RIG_SERIAL_BASE + rigSerialRates.size()) { int serialIdx = event.GetId() - wxID_RIG_SERIAL_BASE; rigSerialRate = rigSerialRates[serialIdx]; resetRig = true; bManaged = true; } if (event.GetId() == wxID_RIG_PORT) { if (rigPortDialog == nullptr) { rigPortDialog = new PortSelectorDialog(this, wxID_ANY, rigPort); rigPortDialog->ShowModal(); } } if (event.GetId() == wxID_RIG_TOGGLE) { resetRig = false; if (!wxGetApp().rigIsActive()) { enableRig(); } else { disableRig(); } bManaged = true; } if (event.GetId() == wxID_RIG_SDR_IF) { if (devInfo != nullptr) { std::string deviceId = devInfo->getDeviceId(); DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); long long freqRigIF = wxGetNumberFromUser("Rig SDR-IF Frequency", "Frequency (Hz)", "Frequency", devConfig->getRigIF(rigModel), 0, 2000000000); if (freqRigIF != -1) { rigSDRIF = freqRigIF; devConfig->setRigIF(rigModel, rigSDRIF); } if (rigSDRIF && wxGetApp().rigIsActive()) { wxGetApp().lockFrequency(rigSDRIF); } else { wxGetApp().unlockFrequency(); } } bManaged = true; } if (event.GetId() == wxID_RIG_CONTROL) { if (wxGetApp().rigIsActive()) { RigThread *rt = wxGetApp().getRigThread(); rt->setControlMode(!rt->getControlMode()); rigControlMenuItem->Check(rt->getControlMode()); wxGetApp().getConfig()->setRigControlMode(rt->getControlMode()); } else { wxGetApp().getConfig()->setRigControlMode(rigControlMenuItem->IsChecked()); } bManaged = true; } if (event.GetId() == wxID_RIG_FOLLOW) { if (wxGetApp().rigIsActive()) { RigThread *rt = wxGetApp().getRigThread(); rt->setFollowMode(!rt->getFollowMode()); rigFollowMenuItem->Check(rt->getFollowMode()); wxGetApp().getConfig()->setRigFollowMode(rt->getFollowMode()); } else { wxGetApp().getConfig()->setRigFollowMode(rigFollowMenuItem->IsChecked()); } bManaged = true; } if (event.GetId() == wxID_RIG_CENTERLOCK) { if (wxGetApp().rigIsActive()) { RigThread *rt = wxGetApp().getRigThread(); rt->setCenterLock(!rt->getCenterLock()); rigCenterLockMenuItem->Check(rt->getCenterLock()); wxGetApp().getConfig()->setRigCenterLock(rt->getCenterLock()); } else { wxGetApp().getConfig()->setRigCenterLock(rigCenterLockMenuItem->IsChecked()); } bManaged = true; } if (event.GetId() == wxID_RIG_FOLLOW_MODEM) { if (wxGetApp().rigIsActive()) { RigThread *rt = wxGetApp().getRigThread(); rt->setFollowModem(!rt->getFollowModem()); rigFollowModemMenuItem->Check(rt->getFollowModem()); wxGetApp().getConfig()->setRigFollowModem(rt->getFollowModem()); } else { wxGetApp().getConfig()->setRigFollowModem(rigFollowModemMenuItem->IsChecked()); } bManaged = true; } if (wxGetApp().rigIsActive() && resetRig) { wxGetApp().stopRig(); wxGetApp().initRig(rigModel, rigPort, rigSerialRate); } #endif return bManaged; } void AppFrame::OnMenu(wxCommandEvent& event) { if (actionOnMenuAbout(event)) { return; } else if (event.GetId() == wxID_SDR_START_STOP) { if (!wxGetApp().getSDRThread()->isTerminated()) { wxGetApp().stopDevice(true, 2000); } else { SDRDeviceInfo *dev = wxGetApp().getDevice(); if (dev != nullptr) { wxGetApp().setDevice(dev, 0); } } } else if (event.GetId() == wxID_LOW_PERF) { lowPerfMode = lowPerfMenuItem->IsChecked(); wxGetApp().getConfig()->setLowPerfMode(lowPerfMode); } else if (event.GetId() == wxID_SET_TIPS ) { if (wxGetApp().getConfig()->getShowTips()) { wxGetApp().getConfig()->setShowTips(false); } else { wxGetApp().getConfig()->setShowTips(true); } } else if (event.GetId() == wxID_SET_IQSWAP) { wxGetApp().getSDRThread()->setIQSwap(!wxGetApp().getSDRThread()->getIQSwap()); } else if (event.GetId() == wxID_SET_FREQ_OFFSET) { //enter in KHz to accomodate > 2GHz shifts for down/upconverters on 32 bit platforms. long ofs = wxGetNumberFromUser("Shift the displayed frequency by this amount of KHz.\ni.e. -125000 for -125 MHz", "Frequency (KHz)", "Frequency Offset", (long long)(wxGetApp().getOffset() / 1000.0) , -2000000000, 2000000000, this); if (ofs != -1) { wxGetApp().setOffset((long long) ofs * 1000); settingsMenuItems[wxID_SET_FREQ_OFFSET]->SetItemLabel(getSettingsLabel("Frequency Offset", std::to_string(wxGetApp().getOffset() / 1000), "KHz")); } } else if (event.GetId() == wxID_SET_DB_OFFSET) { long ofs = wxGetNumberFromUser("Shift the displayed RF power level by this amount.\ni.e. -30 for -30 dB", "Decibels (dB)", "Power Level Offset", wxGetApp().getConfig()->getDBOffset(), -1000, 1000, this); if (ofs != -1) { wxGetApp().getConfig()->setDBOffset(ofs); settingsMenuItems[wxID_SET_DB_OFFSET]->SetItemLabel(getSettingsLabel("Power Level Offset", std::to_string(wxGetApp().getConfig()->getDBOffset()), "dB")); } } else if (actionOnMenuAGC(event)) { return; } else if (event.GetId() == wxID_SDR_DEVICES) { wxGetApp().deviceSelector(); } else if (event.GetId() == wxID_SET_PPM) { long ofs = wxGetNumberFromUser("Frequency correction for device in PPM.\ni.e. -51 for -51 PPM\n\nNote: you can adjust PPM interactively\nby holding ALT over the frequency tuning bar.\n", "Parts per million (PPM)", "Frequency Correction", wxGetApp().getPPM(), -1000, 1000, this); wxGetApp().setPPM(ofs); settingsMenuItems[wxID_SET_PPM]->SetItemLabel(getSettingsLabel("Device PPM", std::to_string(wxGetApp().getPPM()), "ppm")); } else if (actionOnMenuLoadSave(event)) { return; } else if (actionOnMenuReset(event)) { return; } else if (event.GetId() == wxID_CLOSE || event.GetId() == wxID_EXIT) { Close(false); } else if (actionOnMenuSettings(event)) { return; } else if (actionOnMenuSampleRate(event)) { return; } else if (actionOnMenuAudioSampleRate(event)) { return; } else if (actionOnMenuRecording(event)) { return; } else if (actionOnMenuDisplay(event)) { return; } //Optional : Rig else if (actionOnMenuRig(event)) { return; } } void AppFrame::OnClose(wxCloseEvent& event) { wxGetApp().closeDeviceSelector(); if (aboutDlg) { aboutDlg->Destroy(); } if (wxGetApp().getDemodSpectrumProcessor()) { wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodSpectrumCanvas->getVisualDataQueue()); wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodWaterfallCanvas->getVisualDataQueue()); } wxGetApp().getSpectrumProcessor()->removeOutput(spectrumCanvas->getVisualDataQueue()); if (saveDisabled) { event.Skip(); return; } #ifdef __APPLE__ if (this->GetPosition().y > 0) { wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize()); wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized()); } #else wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize()); wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized()); #endif wxGetApp().getConfig()->setTheme(ThemeMgr::mgr.getTheme()); wxGetApp().getConfig()->setFontScale(GLFont::getScale()); wxGetApp().getConfig()->setSnap(wxGetApp().getFrequencySnap()); wxGetApp().getConfig()->setCenterFreq(wxGetApp().getFrequency()); wxGetApp().getConfig()->setSpectrumAvgSpeed(wxGetApp().getSpectrumProcessor()->getFFTAverageRate()); wxGetApp().getConfig()->setWaterfallLinesPerSec(waterfallDataThread->getLinesPerSecond()); wxGetApp().getConfig()->setManualDevices(SDREnumerator::getManuals()); wxGetApp().getConfig()->setModemPropsCollapsed(modemProps->isCollapsed()); wxGetApp().getConfig()->setMainSplit(mainSplitter->GetSashPosition()); wxGetApp().getConfig()->setVisSplit(mainVisSplitter->GetSashPosition()); if (!hideBookmarksItem->IsChecked()) wxGetApp().getConfig()->setBookmarkSplit(bookmarkSplitter->GetSashPosition()); wxGetApp().getConfig()->setBookmarksVisible(!hideBookmarksItem->IsChecked()); #ifdef USE_HAMLIB wxGetApp().getConfig()->setRigEnabled(rigEnableMenuItem->IsChecked()); wxGetApp().getConfig()->setRigModel(rigModel); wxGetApp().getConfig()->setRigRate(rigSerialRate); wxGetApp().getConfig()->setRigPort(rigPort); wxGetApp().getConfig()->setRigFollowMode(rigFollowMenuItem->IsChecked()); wxGetApp().getConfig()->setRigControlMode(rigControlMenuItem->IsChecked()); wxGetApp().getConfig()->setRigCenterLock(rigCenterLockMenuItem->IsChecked()); wxGetApp().getConfig()->setRigFollowModem(rigFollowModemMenuItem->IsChecked()); #endif wxGetApp().getConfig()->save(); wxGetApp().getBookmarkMgr().saveToFile("bookmarks.xml"); event.Skip(); } void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) { new AppFrame(); } void AppFrame::OnIdle(wxIdleEvent& event) { if (deviceChanged.load()) { updateDeviceParams(); } //Refresh the current TX antenna on, if any: if ((antennaMenuItems.find(wxID_ANTENNA_CURRENT_TX) != antennaMenuItems.end()) && devInfo) { std::string actualTxAntenna = devInfo->getAntennaName(SOAPY_SDR_TX, 0); if (currentTXantennaName != actualTxAntenna) { currentTXantennaName = actualTxAntenna; antennaMenuItems[wxID_ANTENNA_CURRENT_TX]->SetItemLabel(getSettingsLabel("TX Antenna", currentTXantennaName)); } } DemodulatorInstancePtr demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (demod && demod->isModemInitialized()) { if (demod->isTracking()) { if (spectrumCanvas->getViewState()) { long long diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth()/2) + (demod->getBandwidth()/4); if (diff > spectrumCanvas->getBandwidth()/2) { if (demod->getBandwidth() > (int)spectrumCanvas->getBandwidth()) { diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()); } else { diff = diff - spectrumCanvas->getBandwidth()/2; } spectrumCanvas->moveCenterFrequency((demod->getFrequency() < spectrumCanvas->getCenterFrequency())?diff:-diff); demod->setTracking(false); } } else { demod->setTracking(false); } } if (demod->getBandwidth() != wxGetApp().getDemodMgr().getLastBandwidth()) { wxGetApp().getDemodMgr().setLastBandwidth(demod->getBandwidth()); } if (demod.get() != activeDemodulator) { demodSignalMeter->setInputValue(demod->getSquelchLevel()); demodGainMeter->setInputValue(demod->getGain()); wxGetApp().getDemodMgr().setLastGain(demod->getGain()); int outputDevice = demod->getOutputDevice(); if (scopeCanvas) { scopeCanvas->setDeviceName(outputDevices[outputDevice].name); } // outputDeviceMenuItems[outputDevice]->Check(true); std::string dType = demod->getDemodulatorType(); demodModeSelector->setSelection(dType); #ifdef ENABLE_DIGITAL_LAB demodModeSelectorAdv->setSelection(dType); #endif deltaLockButton->setSelection(demod->isDeltaLock()?1:-1); demodMuteButton->setSelection(demod->isMuted()?1:-1); modemPropertiesUpdated.store(true); demodTuner->setHalfBand(dType=="USB" || dType=="LSB"); } if (!demodWaterfallCanvas || demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { long long centerFreq = demod->getFrequency(); unsigned int demodBw = (unsigned int) ceil((float) demod->getBandwidth() * 2.25); if (demod->getDemodulatorType() == "USB") { demodBw /= 2; centerFreq += demod->getBandwidth() / 4; } if (demod->getDemodulatorType() == "LSB") { demodBw /= 2; centerFreq -= demod->getBandwidth() / 4; } if (demodBw > wxGetApp().getSampleRate() / 2) { demodBw = wxGetApp().getSampleRate() / 2; } if (demodBw < 20000) { demodBw = 20000; } if (demodWaterfallCanvas && centerFreq != demodWaterfallCanvas->getCenterFrequency()) { demodWaterfallCanvas->setCenterFrequency(centerFreq); demodSpectrumCanvas->setCenterFrequency(centerFreq); } std::string dSelection = demodModeSelector->getSelectionLabel(); #ifdef ENABLE_DIGITAL_LAB std::string dSelectionadv = demodModeSelectorAdv->getSelectionLabel(); // basic demodulators if (dSelection != "" && dSelection != demod->getDemodulatorType()) { demod->setDemodulatorType(dSelection); demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB"); demodModeSelectorAdv->setSelection(-1); } // advanced demodulators else if (dSelectionadv != "" && dSelectionadv != demod->getDemodulatorType()) { demod->setDemodulatorType(dSelectionadv); demodTuner->setHalfBand(false); demodModeSelector->setSelection(-1); } #else // basic demodulators if (dSelection != "" && dSelection != demod->getDemodulatorType()) { demod->setDemodulatorType(dSelection); demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB"); } #endif int muteMode = demodMuteButton->getSelection(); if (demodMuteButton->modeChanged()) { if (demod->isMuted() && muteMode == -1) { demod->setMuted(false); } else if (!demod->isMuted() && muteMode == 1) { demod->setMuted(true); } wxGetApp().getDemodMgr().setLastMuted(demod->isMuted()); demodMuteButton->clearModeChanged(); } else { if (demod->isMuted() && muteMode == -1) { demodMuteButton->setSelection(1); wxGetApp().getDemodMgr().setLastMuted(demod->isMuted()); demodMuteButton->Refresh(); } else if (!demod->isMuted() && muteMode == 1) { demodMuteButton->setSelection(-1); wxGetApp().getDemodMgr().setLastMuted(demod->isMuted()); demodMuteButton->Refresh(); } } int deltaMode = deltaLockButton->getSelection(); if (deltaLockButton->modeChanged()) { if (demod->isDeltaLock() && deltaMode == -1) { demod->setDeltaLock(false); } else if (!demod->isDeltaLock() && deltaMode == 1) { demod->setDeltaLockOfs(demod->getFrequency()-wxGetApp().getFrequency()); demod->setDeltaLock(true); } wxGetApp().getDemodMgr().setLastDeltaLock(demod->isDeltaLock()); deltaLockButton->clearModeChanged(); } else { if (demod->isDeltaLock() && deltaMode == -1) { deltaLockButton->setSelection(1); wxGetApp().getDemodMgr().setLastDeltaLock(true); deltaLockButton->Refresh(); } else if (!demod->isDeltaLock() && deltaMode == 1) { deltaLockButton->setSelection(-1); wxGetApp().getDemodMgr().setLastDeltaLock(false); deltaLockButton->Refresh(); } } int soloMode = soloModeButton->getSelection(); if (soloModeButton->modeChanged()) { if (soloMode == 1) { wxGetApp().setSoloMode(true); } else { wxGetApp().setSoloMode(false); } soloModeButton->clearModeChanged(); } else { if (wxGetApp().getSoloMode() != (soloMode==1)) { soloModeButton->setSelection(wxGetApp().getSoloMode()?1:-1); soloModeButton->Refresh(); } } if (demodWaterfallCanvas) { demodWaterfallCanvas->setBandwidth(demodBw); demodSpectrumCanvas->setBandwidth(demodBw); } } demodSignalMeter->setLevel(demod->getSignalLevel()); demodSignalMeter->setMin(demod->getSignalFloor()); demodSignalMeter->setMax(demod->getSignalCeil()); demodGainMeter->setLevel(demod->getGain()); if (demodSignalMeter->inputChanged()) { demod->setSquelchLevel(demodSignalMeter->getInputValue()); } if (demodGainMeter->inputChanged()) { demod->setGain(demodGainMeter->getInputValue()); demodGainMeter->setLevel(demodGainMeter->getInputValue()); } activeDemodulator = demod.get(); } else if (demod) { // Wait state for current demodulator modem to activate.. } else { DemodulatorMgr *mgr = &wxGetApp().getDemodMgr(); std::string dSelection = demodModeSelector->getSelectionLabel(); #ifdef ENABLE_DIGITAL_LAB std::string dSelectionadv = demodModeSelectorAdv->getSelectionLabel(); // basic demodulators if (dSelection != "" && dSelection != mgr->getLastDemodulatorType()) { mgr->setLastDemodulatorType(dSelection); mgr->setLastBandwidth(Modem::getModemDefaultSampleRate(dSelection)); demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB"); demodModeSelectorAdv->setSelection(-1); } // advanced demodulators else if(dSelectionadv != "" && dSelectionadv != mgr->getLastDemodulatorType()) { mgr->setLastDemodulatorType(dSelectionadv); mgr->setLastBandwidth(Modem::getModemDefaultSampleRate(dSelectionadv)); demodTuner->setHalfBand(false); demodModeSelector->setSelection(-1); } #else // basic demodulators if (dSelection != "" && dSelection != mgr->getLastDemodulatorType()) { mgr->setLastDemodulatorType(dSelection); mgr->setLastBandwidth(Modem::getModemDefaultSampleRate(dSelection)); demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB"); } #endif demodGainMeter->setLevel(mgr->getLastGain()); if (demodSignalMeter->inputChanged()) { mgr->setLastSquelchLevel(demodSignalMeter->getInputValue()); } if (demodGainMeter->inputChanged()) { mgr->setLastGain(demodGainMeter->getInputValue()); demodGainMeter->setLevel(demodGainMeter->getInputValue()); } if (demodWaterfallCanvas && wxGetApp().getFrequency() != demodWaterfallCanvas->getCenterFrequency()) { demodWaterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); if (demodSpectrumCanvas) { demodSpectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); } } if (spectrumCanvas->getViewState() && abs(wxGetApp().getFrequency()-spectrumCanvas->getCenterFrequency()) > (wxGetApp().getSampleRate()/2)) { spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); } if (demodMuteButton->modeChanged()) { int muteMode = demodMuteButton->getSelection(); if (muteMode == -1) { wxGetApp().getDemodMgr().setLastMuted(false); } else if (muteMode == 1) { wxGetApp().getDemodMgr().setLastMuted(true); } demodMuteButton->clearModeChanged(); } } if (scopeCanvas) { scopeCanvas->setPPMMode(demodTuner->isAltDown()); wxGetApp().getScopeProcessor()->setScopeEnabled(scopeCanvas->scopeVisible()); wxGetApp().getScopeProcessor()->setSpectrumEnabled(scopeCanvas->spectrumVisible()); wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0)); wxGetApp().getScopeProcessor()->run(); } SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor(); if (spectrumAvgMeter->inputChanged()) { float val = spectrumAvgMeter->getInputValue(); if (val < 0.01) { val = 0.01f; } if (val > 0.99) { val = 0.99f; } spectrumAvgMeter->setLevel(val); proc->setFFTAverageRate(val); GetStatusBar()->SetStatusText(wxString::Format(wxT("Spectrum averaging speed changed to %0.2f%%."),val*100.0)); } SpectrumVisualProcessor *dproc = wxGetApp().getDemodSpectrumProcessor(); if (dproc) { dproc->setView(demodWaterfallCanvas->getViewState(), demodWaterfallCanvas->getCenterFrequency(),demodWaterfallCanvas->getBandwidth()); } SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor(); if (waterfallSpeedMeter->inputChanged()) { float val = waterfallSpeedMeter->getInputValue(); waterfallSpeedMeter->setLevel(val); waterfallDataThread->setLinesPerSecond((int)ceil(val*val)); waterfallCanvas->setLinesPerSecond((int)ceil(val*val)); GetStatusBar()->SetStatusText(wxString::Format(wxT("Waterfall max speed changed to %d lines per second."),(int)ceil(val*val))); } wproc->setView(waterfallCanvas->getViewState(), waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth()); wxGetApp().getSDRPostThread()->setIQVisualRange(waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth()); proc->setView(wproc->isView(), wproc->getCenterFrequency(), wproc->getBandwidth()); demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (modemPropertiesUpdated.load() && demod && demod->isModemInitialized()) { //reset notification flag modemPropertiesUpdated.store(false); modemProps->initProperties(demod->getModemArgs(), demod); modemProps->updateTheme(); demodTray->Layout(); modemProps->fitColumns(); #if ENABLE_DIGITAL_LAB if (demod->getModemType() == "digital") { ModemDigitalOutputConsole *outp = (ModemDigitalOutputConsole *)demod->getOutput(); if (!outp->getDialog()) { outp->setTitle(demod->getDemodulatorType() + ": " + frequencyToStr(demod->getFrequency())); outp->setDialog(new DigitalConsole(this, outp)) ; } demod->showOutput(); } #endif } else if (!demod && modemPropertiesUpdated.load()) { ModemArgInfoList dummyInfo; modemProps->initProperties(dummyInfo, nullptr); modemProps->updateTheme(); demodTray->Layout(); } if (modemProps->IsShown() && modemProps->isCollapsed() && modemProps->GetMinWidth() > 22) { modemProps->SetMinSize(wxSize(APPFRAME_MODEMPROPS_MINSIZE,-1)); modemProps->SetMaxSize(wxSize(APPFRAME_MODEMPROPS_MINSIZE,-1)); demodTray->Layout(); modemProps->fitColumns(); } else if (modemProps->IsShown() && !modemProps->isCollapsed() && modemProps->GetMinWidth() < 200) { modemProps->SetMinSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE,-1)); modemProps->SetMaxSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE,-1)); demodTray->Layout(); modemProps->fitColumns(); } int peakHoldMode = peakHoldButton->getSelection(); if (peakHoldButton->modeChanged()) { wxGetApp().getSpectrumProcessor()->setPeakHold(peakHoldMode == 1); //make the peak hold act on the current dmod also, like a zoomed-in version. if (wxGetApp().getDemodSpectrumProcessor()) { wxGetApp().getDemodSpectrumProcessor()->setPeakHold(peakHoldMode == 1); } peakHoldButton->clearModeChanged(); } #if USE_HAMLIB if (rigEnableMenuItem->IsChecked()) { if (!wxGetApp().rigIsActive()) { rigEnableMenuItem->Check(false); wxGetApp().getConfig()->setRigEnabled(false); } } #endif #ifdef _WIN32 if (scopeCanvas && scopeCanvas->HasFocus()) { waterfallCanvas->SetFocus(); } #endif if (!this->IsActive()) { std::this_thread::sleep_for(std::chrono::milliseconds(30)); } else { if (lowPerfMode) { std::this_thread::sleep_for(std::chrono::milliseconds(30)); } else { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } event.RequestMore(); } void AppFrame::OnDoubleClickSash(wxSplitterEvent& event) { wxWindow *a, *b; wxSplitterWindow *w = NULL; float g = 0.5; if (event.GetId() == wxID_MAIN_SPLITTER) { w = mainSplitter; g = 10.0f/37.0f; } else if (event.GetId() == wxID_VIS_SPLITTER) { w = mainVisSplitter; g = 6.0f/25.0f; } if (w != NULL) { a = w->GetWindow1(); b = w->GetWindow2(); w->Unsplit(); w->SetSashGravity(g); wxSize s = w->GetSize(); w->SplitHorizontally(a, b, int(float(s.GetHeight()) * g)); } event.Veto(); } void AppFrame::OnUnSplit(wxSplitterEvent& event) { event.Veto(); } void AppFrame::OnAboutDialogClose(wxCommandEvent& event) { aboutDlg->Destroy(); aboutDlg = nullptr; } void AppFrame::saveSession(std::string fileName) { DataTree s("cubicsdr_session"); DataNode *header = s.rootNode()->newChild("header"); //save as wstring to prevent problems header->newChild("version")->element()->set(wxString(CUBICSDR_VERSION).ToStdWstring()); *header->newChild("center_freq") = wxGetApp().getFrequency(); *header->newChild("sample_rate") = wxGetApp().getSampleRate(); *header->newChild("solo_mode") = wxGetApp().getSoloMode()?1:0; if (waterfallCanvas->getViewState()) { DataNode *viewState = header->newChild("view_state"); *viewState->newChild("center_freq") = waterfallCanvas->getCenterFrequency(); *viewState->newChild("bandwidth") = waterfallCanvas->getBandwidth(); } DataNode *demods = s.rootNode()->newChild("demodulators"); //make a local copy snapshot of the list std::vector instances = wxGetApp().getDemodMgr().getDemodulators(); for (auto instance : instances) { DataNode *demod = demods->newChild("demodulator"); wxGetApp().getDemodMgr().saveInstance(demod, instance); } //end for demodulators // Make sure the file name actually ends in .xml std::string lcFileName = fileName; std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower); if (lcFileName.find_last_of(".xml") != lcFileName.length()-1) { fileName.append(".xml"); } s.SaveToFileXML(fileName); currentSessionFile = fileName; std::string filePart = fileName.substr(fileName.find_last_of(filePathSeparator) + 1); GetStatusBar()->SetStatusText(wxString::Format(wxT("Saved session: %s"), currentSessionFile.c_str())); SetTitle(wxString::Format(wxT("%s: %s"), CUBICSDR_TITLE, filePart.c_str())); } bool AppFrame::loadSession(std::string fileName) { DataTree l; if (!l.LoadFromFileXML(fileName)) { return false; } //Check if it is a session file, read the root node. if (l.rootNode()->getName() != "cubicsdr_session") { return false; } wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, false); wxGetApp().getDemodMgr().terminateAll(); try { if (!l.rootNode()->hasAnother("header")) { return false; } DataNode *header = l.rootNode()->getNext("header"); if (header->hasAnother("version")) { //"Force" the retreiving of the value as string, even if its look like a number internally ! (ex: "0.2.0") DataNode *versionNode = header->getNext("version"); std::wstring version; try { versionNode->element()->get(version); std::cout << "Loading session file version: '" << version << "'..." << std::endl; } catch (DataTypeMismatchException* e) { //this is for managing the old session format NOT encoded as std:wstring, //force current version std::cout << "Warning while Loading session file version, probably old format :'" << e->what() << "' please consider re-saving the current session..." << std::endl; version = wxString(CUBICSDR_VERSION).ToStdWstring(); } } if (header->hasAnother("sample_rate")) { long sample_rate = *header->getNext("sample_rate"); SDRDeviceInfo *dev = wxGetApp().getSDRThread()->getDevice(); if (dev) { //retreive the available sample rates. A valid previously chosen manual //value is constrained within these limits. If it doesn't behave, lets the device choose //for us. long minRate = MANUAL_SAMPLE_RATE_MIN; long maxRate = MANUAL_SAMPLE_RATE_MAX; std::vector sampleRates = dev->getSampleRates(SOAPY_SDR_RX, 0); if (sampleRates.size()) { minRate = sampleRates.front(); maxRate = sampleRates.back(); } //If it is beyond limits, make device choose a reasonable value if (sample_rate < minRate || sample_rate > maxRate) { sample_rate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, sample_rate); } //scan the available sample rates and see if it matches a predifined one int menuIndex = -1; for (auto discreteRate : sampleRates) { if (discreteRate == sample_rate) { menuIndex++; //activate Bandwidth Menu entry matching this predefined sample_rate. sampleRateMenuItems[wxID_BANDWIDTH_BASE + menuIndex]->Check(true); break; } } //end for //this is a manual entry if (menuIndex == -1) { manualSampleRate = sample_rate; sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true); // Apply the manual value, activate the menu entry sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->SetItemLabel(wxString("Manual Entry : ") + frequencyToStr(sample_rate)); sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true); } //update applied value wxGetApp().setSampleRate(sample_rate); deviceChanged.store(true); } else { wxGetApp().setSampleRate(sample_rate); } } if (header->hasAnother("solo_mode")) { int solo_mode_activated = *header->getNext("solo_mode"); wxGetApp().setSoloMode((solo_mode_activated > 0) ? true : false); } else { wxGetApp().setSoloMode(false); } DemodulatorInstancePtr loadedActiveDemod = nullptr; DemodulatorInstancePtr newDemod = nullptr; if (l.rootNode()->hasAnother("demodulators")) { DataNode *demodulators = l.rootNode()->getNext("demodulators"); std::vector demodsLoaded; while (demodulators->hasAnother("demodulator")) { DataNode *demod = demodulators->getNext("demodulator"); if (!demod->hasAnother("bandwidth") || !demod->hasAnother("frequency")) { continue; } newDemod = wxGetApp().getDemodMgr().loadInstance(demod); if (demod->hasAnother("active")) { loadedActiveDemod = newDemod; } newDemod->run(); newDemod->setActive(true); demodsLoaded.push_back(newDemod); } if (demodsLoaded.size()) { wxGetApp().notifyDemodulatorsChanged(); } } // if l.rootNode()->hasAnother("demodulators") if (header->hasAnother("center_freq")) { long long center_freq = *header->getNext("center_freq"); wxGetApp().setFrequency(center_freq); // std::cout << "\tCenter Frequency: " << center_freq << std::endl; } if (header->hasAnother("view_state")) { DataNode *viewState = header->getNext("view_state"); if (viewState->hasAnother("center_freq") && viewState->hasAnother("bandwidth")) { long long center_freq = *viewState->getNext("center_freq"); int bandwidth = *viewState->getNext("bandwidth"); spectrumCanvas->setView(center_freq, bandwidth); waterfallCanvas->setView(center_freq, bandwidth); } } else { spectrumCanvas->disableView(); waterfallCanvas->disableView(); spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); } if (loadedActiveDemod || newDemod) { wxGetApp().getDemodMgr().setActiveDemodulator(loadedActiveDemod?loadedActiveDemod:newDemod, false); } } catch (DataTypeMismatchException &e) { std::cout << e.what() << std::endl; return false; } currentSessionFile = fileName; std::string filePart = fileName.substr(fileName.find_last_of(filePathSeparator) + 1); GetStatusBar()->SetStatusText(wxString::Format(wxT("Loaded session file: %s"), currentSessionFile.c_str())); SetTitle(wxString::Format(wxT("%s: %s"), CUBICSDR_TITLE, filePart.c_str())); wxGetApp().getBookmarkMgr().updateActiveList(); return true; } FFTVisualDataThread *AppFrame::getWaterfallDataThread() { return waterfallDataThread; } void AppFrame::notifyUpdateModemProperties() { modemPropertiesUpdated.store(true); } void AppFrame::setMainWaterfallFFTSize(int fftSize) { wxGetApp().getSpectrumProcessor()->setFFTSize(fftSize); spectrumCanvas->setFFTSize(fftSize); waterfallDataThread->getProcessor()->setFFTSize(fftSize); waterfallCanvas->setFFTSize(fftSize); } void AppFrame::setScopeDeviceName(std::string deviceName) { if (scopeCanvas) { scopeCanvas->setDeviceName(deviceName); } } void AppFrame::refreshGainUI() { gainCanvas->updateGainUI(); gainCanvas->Refresh(); } bool AppFrame::isUserDemodBusy() { return (modemProps && modemProps->isMouseInView()) || (waterfallCanvas->isMouseInView() && waterfallCanvas->isMouseDown()) || (demodWaterfallCanvas && demodWaterfallCanvas->isMouseInView() && demodWaterfallCanvas->isMouseDown()) || (wxGetApp().getDemodMgr().getLastActiveDemodulator() && wxGetApp().getDemodMgr().getActiveDemodulator() && wxGetApp().getDemodMgr().getLastActiveDemodulator() != wxGetApp().getDemodMgr().getActiveDemodulator()); } BookmarkView *AppFrame::getBookmarkView() { return bookmarkView; } void AppFrame::disableSave(bool state) { saveDisabled = state; } #ifdef _WIN32 bool AppFrame::canFocus() { return (!wxGetApp().isDeviceSelectorOpen() && (!modemProps || !modemProps->isMouseInView())); } #endif FrequencyDialog::FrequencyDialogTarget AppFrame::getFrequencyDialogTarget() { FrequencyDialog::FrequencyDialogTarget target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_DEFAULT; if (waterfallSpeedMeter->getMouseTracker()->mouseInView()) { target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_WATERFALL_LPS; } else if (spectrumAvgMeter->getMouseTracker()->mouseInView()) { target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_SPECTRUM_AVG; } else if (demodTuner->getMouseTracker()->mouseInView()) { switch (demodTuner->getHoverState()) { case TuningCanvas::ActiveState::TUNING_HOVER_BW: target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_BANDWIDTH; break; case TuningCanvas::ActiveState::TUNING_HOVER_FREQ: target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_FREQ; break; case TuningCanvas::ActiveState::TUNING_HOVER_CENTER: default: target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_DEFAULT; break; } } else if (gainCanvas->getMouseTracker()->mouseInView()) { target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_GAIN; } return target; } void AppFrame::gkNudgeLeft(DemodulatorInstancePtr demod, int snap) { if (demod) { demod->setFrequency(demod->getFrequency()-snap); demod->updateLabel(demod->getFrequency()); } } void AppFrame::gkNudgeRight(DemodulatorInstancePtr demod, int snap) { if (demod) { demod->setFrequency(demod->getFrequency()+snap); demod->updateLabel(demod->getFrequency()); } } int AppFrame::OnGlobalKeyDown(wxKeyEvent &event) { if (!this->IsActive()) { return -1; } #ifdef USE_HAMLIB if (rigPortDialog != nullptr) { return -1; } #endif if (modemProps && (modemProps->HasFocus() || modemProps->isMouseInView())) { return -1; } if (bookmarkView && bookmarkView->isMouseInView()) { return -1; } DemodulatorInstancePtr demod = nullptr; DemodulatorInstancePtr lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); int snap = wxGetApp().getFrequencySnap(); if (event.ControlDown()) { return 1; } if (event.ShiftDown()) { if (snap != 1) { snap /= 2; } } #ifdef wxHAS_RAW_KEY_CODES switch (event.GetRawKeyCode()) { case 30: gkNudgeRight(lastDemod, snap); return 1; case 33: gkNudgeLeft(lastDemod, snap); return 1; } #endif switch (event.GetKeyCode()) { case WXK_UP: case WXK_NUMPAD_UP: case WXK_DOWN: case WXK_NUMPAD_DOWN: case WXK_LEFT: case WXK_NUMPAD_LEFT: case WXK_RIGHT: case WXK_NUMPAD_RIGHT: waterfallCanvas->OnKeyDown(event); // TODO: Move the stuff from there to here return 1; case 'V': return 1; case ']': gkNudgeRight(lastDemod, snap); return 1; case '[': gkNudgeLeft(lastDemod, snap); return 1; case 'A': case 'F': case 'L': case 'U': case 'S': case 'P': case 'M': case 'R': return 1; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': wxGetApp().showFrequencyInput(getFrequencyDialogTarget(), std::to_string(event.GetKeyCode() - '0')); return 1; break; case WXK_TAB: lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (!lastDemod) { break; } if (event.ShiftDown()) { demod = wxGetApp().getDemodMgr().getPreviousDemodulator(lastDemod); } else { demod = wxGetApp().getDemodMgr().getNextDemodulator(lastDemod); } if (demod) { wxGetApp().getDemodMgr().setActiveDemodulator(nullptr); wxGetApp().getDemodMgr().setActiveDemodulator(demod, false); } return 1; default: break; } //Re-dispatch the key events if the mouse cursor is within a given //widget region, effectively activating its specific key shortcuts, //which else are overriden by this global key handler. if (demodTuner->getMouseTracker()->mouseInView()) { demodTuner->OnKeyDown(event); } else if (waterfallCanvas->getMouseTracker()->mouseInView()) { waterfallCanvas->OnKeyDown(event); } else if (spectrumCanvas->getMouseTracker()->mouseInView()) { spectrumCanvas->OnKeyDown(event); } else if (scopeCanvas->getMouseTracker()->mouseInView()) { scopeCanvas->OnKeyDown(event); } return 1; } int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) { if (!this->IsActive()) { return -1; } #ifdef USE_HAMLIB if (rigPortDialog != nullptr) { return -1; } #endif if (modemProps && (modemProps->HasFocus() || modemProps->isMouseInView())) { return -1; } if (bookmarkView && bookmarkView->isMouseInView()) { return -1; } if (event.ControlDown()) { return 1; } DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); DemodulatorInstancePtr lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); #ifdef wxHAS_RAW_KEY_CODES switch (event.GetRawKeyCode()) { case 30: return 1; case 33: return 1; } #endif switch (event.GetKeyCode()) { case WXK_SPACE: if (!demodTuner->getMouseTracker()->mouseInView()) { wxGetApp().showFrequencyInput(getFrequencyDialogTarget()); return 1; } break; case WXK_UP: case WXK_NUMPAD_UP: case WXK_DOWN: case WXK_NUMPAD_DOWN: case WXK_LEFT: case WXK_NUMPAD_LEFT: case WXK_RIGHT: case WXK_NUMPAD_RIGHT: waterfallCanvas->OnKeyUp(event); return 1; case 'V': if (activeDemod) { lastDemod = activeDemod; } if (lastDemod && lastDemod->isDeltaLock()) { lastDemod->setDeltaLock(false); } else if (lastDemod) { lastDemod->setDeltaLockOfs(lastDemod->getFrequency() - wxGetApp().getFrequency()); lastDemod->setDeltaLock(true); } break; case 'A': demodModeSelector->setSelection("AM"); return 1; break; case 'F': if (demodModeSelector->getSelectionLabel() == "FM") { demodModeSelector->setSelection("FMS"); } else if (demodModeSelector->getSelectionLabel() == "FMS") { demodModeSelector->setSelection("NBFM"); } else if (demodModeSelector->getSelectionLabel() == "NBFM") { demodModeSelector->setSelection("FM"); } return 1; break; case 'L': demodModeSelector->setSelection("LSB"); return 1; break; case 'U': demodModeSelector->setSelection("USB"); return 1; break; case 'S': wxGetApp().setSoloMode(!wxGetApp().getSoloMode()); return 1; break; case 'R': if (event.ShiftDown()) { toggleAllActiveDemodRecording(); } else { toggleActiveDemodRecording(); } break; case 'P': wxGetApp().getSpectrumProcessor()->setPeakHold(!wxGetApp().getSpectrumProcessor()->getPeakHold()); if (wxGetApp().getDemodSpectrumProcessor()) { wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); } peakHoldButton->setSelection(wxGetApp().getSpectrumProcessor()->getPeakHold()?1:0); peakHoldButton->clearModeChanged(); break; case ']': case '[': return 1; case 'M': if (activeDemod) { lastDemod = activeDemod; } if (lastDemod) { lastDemod->setMuted(!lastDemod->isMuted()); } break; default: break; } //Re-dispatch the key events if the mouse cursor is within a given //widget region, effectively activating its specific key shortcuts, //which else are overriden by this global key handler. if (demodTuner->getMouseTracker()->mouseInView()) { demodTuner->OnKeyUp(event); } else if (waterfallCanvas->getMouseTracker()->mouseInView()) { waterfallCanvas->OnKeyUp(event); } else if (spectrumCanvas->getMouseTracker()->mouseInView()) { spectrumCanvas->OnKeyUp(event); } else if (scopeCanvas->getMouseTracker()->mouseInView()) { scopeCanvas->OnKeyUp(event); } // TODO: Catch key-ups outside of original target return 1; } void AppFrame::toggleActiveDemodRecording() { if (!wxGetApp().getConfig()->verifyRecordingPath()) { return; } DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); if (activeDemod) { activeDemod->setRecording(!activeDemod->isRecording()); wxGetApp().getBookmarkMgr().updateActiveList(); } } void AppFrame::toggleAllActiveDemodRecording() { if (!wxGetApp().getConfig()->verifyRecordingPath()) { return; } auto activeDemods = wxGetApp().getDemodMgr().getDemodulators(); bool stateToSet = true; for (auto i : activeDemods) { if (i->isActive() && i->isRecording()) { stateToSet = false; break; } } for (auto i : activeDemods) { if (i->isActive() && i->isRecording() != stateToSet) { i->setRecording(stateToSet); } } } void AppFrame::setWaterfallLinesPerSecond(int lps) { waterfallSpeedMeter->setUserInputValue(sqrt(lps)); } void AppFrame::setSpectrumAvgSpeed(double avg) { spectrumAvgMeter->setUserInputValue(avg); } void AppFrame::setViewState(long long center_freq, int bandwidth) { spectrumCanvas->setView(center_freq, bandwidth); waterfallCanvas->setView(center_freq, bandwidth); } void AppFrame::setViewState() { spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); spectrumCanvas->disableView(); waterfallCanvas->disableView(); } long long AppFrame::getViewCenterFreq() { return waterfallCanvas->getCenterFrequency(); } int AppFrame::getViewBandwidth() { return waterfallCanvas->getBandwidth(); } /* split a string by 'seperator' into a vector of string */ std::vector str_explode(const std::string &seperator, const std::string &in_str) { std::vector vect_out; size_t i = 0, j = 0; size_t seperator_len = seperator.length(); size_t str_len = in_str.length(); while(i < str_len) { j = in_str.find_first_of(seperator,i); if (j == std::string::npos && i < str_len) { j = str_len; } if (j == std::string::npos) { break; } vect_out.push_back(in_str.substr(i,j-i)); i = j; i+=seperator_len; } return vect_out; } void AppFrame::setStatusText(wxWindow* window, std::string statusText) { GetStatusBar()->SetStatusText(statusText); if (wxGetApp().getConfig()->getShowTips()) { if (statusText != lastToolTip) { wxToolTip::Enable(false); window->SetToolTip(statusText); lastToolTip = statusText; wxToolTip::SetDelay(1000); wxToolTip::Enable(true); } } else { window->SetToolTip(""); lastToolTip = ""; } } void AppFrame::setStatusText(std::string statusText, int value) { GetStatusBar()->SetStatusText( wxString::Format(statusText.c_str(), wxNumberFormatter::ToString((long)value, wxNumberFormatter::Style_WithThousandsSep))); } wxString AppFrame::getSettingsLabel(const std::string& settingsName, const std::string& settingsValue, const std::string& settingsSuffix) { size_t itemStringSize = 30; int justifValueSize = itemStringSize - settingsName.length() - 1; std::stringstream full_label; full_label << settingsName + " : "; full_label << std::right << std::setw(justifValueSize); full_label << settingsValue + " " + settingsSuffix; return wxString(full_label.str()); } CubicSDR-0.2.3/src/AppFrame.h000066400000000000000000000212031322677621400155550ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include #include #include "PrimaryGLContext.h" #include "ScopeCanvas.h" #include "SpectrumCanvas.h" #include "WaterfallCanvas.h" #include "MeterCanvas.h" #include "TuningCanvas.h" #include "ModeSelectorCanvas.h" #include "GainCanvas.h" #include "FFTVisualDataThread.h" #include "SDRDeviceInfo.h" #include "ModemProperties.h" //#include "UITestCanvas.h" #include "FrequencyDialog.h" #include "BookmarkView.h" #include "AboutDialog.h" #include "DemodulatorInstance.h" #include "DemodulatorThread.h" #include #define wxID_RT_AUDIO_DEVICE 1000 #define wxID_SET_FREQ_OFFSET 2001 #define wxID_RESET 2002 #define wxID_SET_PPM 2003 #define wxID_SET_TIPS 2004 #define wxID_SET_IQSWAP 2005 #define wxID_SDR_DEVICES 2008 #define wxID_AGC_CONTROL 2009 #define wxID_SDR_START_STOP 2010 #define wxID_LOW_PERF 2011 #define wxID_SET_DB_OFFSET 2012 #define wxID_ABOUT_CUBICSDR 2013 #define wxID_OPEN_BOOKMARKS 2020 #define wxID_SAVE_BOOKMARKS 2021 #define wxID_SAVEAS_BOOKMARKS 2022 #define wxID_RESET_BOOKMARKS 2023 #define wxID_MAIN_SPLITTER 2050 #define wxID_VIS_SPLITTER 2051 #define wxID_BM_SPLITTER 2052 #define wxID_THEME_DEFAULT 2100 #define wxID_THEME_SHARP 2101 #define wxID_THEME_BW 2102 #define wxID_THEME_RAD 2103 #define wxID_THEME_TOUCH 2104 #define wxID_THEME_HD 2105 #define wxID_THEME_RADAR 2106 #define wxID_DISPLAY_BOOKMARKS 2107 #define wxID_BANDWIDTH_BASE 2150 #define wxID_BANDWIDTH_MANUAL_DIALOG 2199 #define wxID_BANDWIDTH_MANUAL 2200 #define wxID_DISPLAY_BASE 2250 #define wxID_SETTINGS_BASE 2300 #define wxID_ANTENNA_CURRENT 2500 #define wxID_ANTENNA_CURRENT_TX 2501 #define wxID_ANTENNAS_BASE 2502 #define wxID_DEVICE_ID 3500 #define wxID_RECORDING_PATH 8500 #define wxID_RECORDING_SQUELCH_BASE 8501 #define wxID_RECORDING_SQUELCH_SILENCE 8502 #define wxID_RECORDING_SQUELCH_SKIP 8503 #define wxID_RECORDING_SQUELCH_ALWAYS 8504 #define wxID_RECORDING_FILE_TIME_LIMIT 8505 #define wxID_AUDIO_BANDWIDTH_BASE 9000 #define wxID_AUDIO_DEVICE_MULTIPLIER 50 #ifdef USE_HAMLIB #define wxID_RIG_TOGGLE 11900 #define wxID_RIG_PORT 11901 #define wxID_RIG_SDR_IF 11902 #define wxID_RIG_CONTROL 11903 #define wxID_RIG_FOLLOW 11904 #define wxID_RIG_CENTERLOCK 11905 #define wxID_RIG_FOLLOW_MODEM 11906 #define wxID_RIG_SERIAL_BASE 11950 #define wxID_RIG_MODEL_BASE 12000 #endif #ifdef USE_HAMLIB class PortSelectorDialog; #endif // Define a new frame type class AppFrame: public wxFrame { public: AppFrame(); ~AppFrame(); wxMenu *makeFileMenu(); wxMenu *makeRecordingMenu(); void updateRecordingMenu(); void initDeviceParams(SDRDeviceInfo *devInfo); void updateDeviceParams(); void saveSession(std::string fileName); bool loadSession(std::string fileName); FFTVisualDataThread *getWaterfallDataThread(); void notifyUpdateModemProperties(); void setMainWaterfallFFTSize(int fftSize); void setScopeDeviceName(std::string deviceName); void gkNudgeLeft(DemodulatorInstancePtr demod, int snap); void gkNudgeRight(DemodulatorInstancePtr demod, int snap); int OnGlobalKeyDown(wxKeyEvent &event); int OnGlobalKeyUp(wxKeyEvent &event); void toggleActiveDemodRecording(); void toggleAllActiveDemodRecording(); void setWaterfallLinesPerSecond(int lps); void setSpectrumAvgSpeed(double avg); FrequencyDialog::FrequencyDialogTarget getFrequencyDialogTarget(); void refreshGainUI(); void setViewState(long long center_freq, int bandwidth); void setViewState(); long long getViewCenterFreq(); int getViewBandwidth(); bool isUserDemodBusy(); BookmarkView *getBookmarkView(); void disableSave(bool state); //call this in case the main UI is not //the origin of device changes / sample rate by operator, //and must be notified back to update its UI elements //(ex: SDR Devices dialog changing the configuration) void notifyDeviceChanged(); #ifdef _WIN32 bool canFocus(); #endif //set tooltip to window void setStatusText(wxWindow* window, std::string statusText); void setStatusText(std::string statusText, int value); #ifdef USE_HAMLIB void setRigControlPort(std::string portName); void dismissRigControlPortDialog(); #endif private: void OnMenu(wxCommandEvent& event); void OnClose(wxCloseEvent& event); void OnNewWindow(wxCommandEvent& event); void OnIdle(wxIdleEvent& event); void OnDoubleClickSash(wxSplitterEvent& event); void OnUnSplit(wxSplitterEvent& event); void OnAboutDialogClose(wxCommandEvent& event); //actionXXXX manage menu actions, return true if the event has been //treated. bool actionOnMenuAbout(wxCommandEvent& event); bool actionOnMenuReset(wxCommandEvent& event); bool actionOnMenuSettings(wxCommandEvent& event); bool actionOnMenuAGC(wxCommandEvent& event); bool actionOnMenuSampleRate(wxCommandEvent& event); bool actionOnMenuAudioSampleRate(wxCommandEvent& event); bool actionOnMenuDisplay(wxCommandEvent& event); bool actionOnMenuLoadSave(wxCommandEvent& event); bool actionOnMenuRecording(wxCommandEvent& event); bool actionOnMenuRig(wxCommandEvent& event); wxString getSettingsLabel(const std::string& settingsName, const std::string& settingsValue, const std::string& settingsSuffix = ""); ScopeCanvas *scopeCanvas; SpectrumCanvas *spectrumCanvas; WaterfallCanvas *waterfallCanvas; ModeSelectorCanvas *demodModeSelector; #ifdef ENABLE_DIGITAL_LAB ModeSelectorCanvas *demodModeSelectorAdv; #endif SpectrumCanvas *demodSpectrumCanvas; WaterfallCanvas *demodWaterfallCanvas; MeterCanvas *demodSignalMeter; MeterCanvas *demodGainMeter; TuningCanvas *demodTuner; // UITestCanvas *testCanvas; MeterCanvas *spectrumAvgMeter; MeterCanvas *waterfallSpeedMeter; ModeSelectorCanvas *demodMuteButton, *peakHoldButton, *soloModeButton, *deltaLockButton; GainCanvas *gainCanvas; wxSizerItem *gainSizerItem, *gainSpacerItem; wxSplitterWindow *mainVisSplitter, *mainSplitter, *bookmarkSplitter; wxBoxSizer *demodTray; BookmarkView *bookmarkView; //Use a raw pointer here to prevent a dangling reference DemodulatorInstance* activeDemodulator; std::vector devices; std::map inputDevices; std::map outputDevices; std::map outputDeviceMenuItems; std::map sampleRateMenuItems; std::map antennaMenuItems; //depending on context, maps the item id to wxMenuItem*, //OR the submenu item id to its parent wxMenuItem*. std::map settingsMenuItems; std::map audioSampleRateMenuItems; // std::map recordingMenuItems; std::map directSamplingMenuItems; wxMenuBar *menuBar; wxMenu *sampleRateMenu = nullptr; wxMenu *displayMenu = nullptr; wxMenuItem *agcMenuItem = nullptr; wxMenuItem *iqSwapMenuItem = nullptr; wxMenuItem *lowPerfMenuItem = nullptr; wxMenu *fileMenu = nullptr; wxMenu *settingsMenu = nullptr; wxMenu *recordingMenu = nullptr; SoapySDR::ArgInfoList settingArgs; int settingsIdMax; std::vector sampleRates; long manualSampleRate = -1; std::vector antennaNames; std::string currentTXantennaName; std::string currentSessionFile; std::string currentBookmarkFile; FFTVisualDataThread *waterfallDataThread; std::thread *t_FFTData; SDRDeviceInfo *devInfo; std::atomic_bool deviceChanged; ModemProperties *modemProps; std::atomic_bool modemPropertiesUpdated; wxMenuItem *showTipMenuItem; bool lowPerfMode; wxMenuItem *hideBookmarksItem; bool saveDisabled; AboutDialog *aboutDlg; std::string lastToolTip; #ifdef USE_HAMLIB void enableRig(); void disableRig(); wxMenu *rigMenu; wxMenuItem *rigEnableMenuItem; wxMenuItem *rigPortMenuItem; wxMenuItem *rigControlMenuItem; wxMenuItem *rigFollowMenuItem; wxMenuItem *rigCenterLockMenuItem; wxMenuItem *rigFollowModemMenuItem; std::map rigSerialMenuItems; std::map rigModelMenuItems; int rigModel; int rigSerialRate; long long rigSDRIF; std::vector rigSerialRates; std::string rigPort; int numRigs; PortSelectorDialog *rigPortDialog; #endif wxDECLARE_EVENT_TABLE(); }; CubicSDR-0.2.3/src/BookmarkMgr.cpp000066400000000000000000000457621322677621400166500ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "BookmarkMgr.h" #include "CubicSDR.h" #include "DataTree.h" #include #define BOOKMARK_RECENTS_MAX 25 BookmarkEntry::~BookmarkEntry() { delete node; } BookmarkMgr::BookmarkMgr() { rangesSorted = false; } //represents an empty BookMarkList that is returned by reference by some functions. const BookmarkList BookmarkMgr::emptyResults; void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup, bool useFullpath) { DataTree s("cubicsdr_bookmarks"); DataNode *header = s.rootNode()->newChild("header"); header->newChild("version")->element()->set(wxString(CUBICSDR_VERSION).ToStdWstring()); DataNode *branches = s.rootNode()->newChild("branches"); *branches->newChild("active") = wxGetApp().getAppFrame()->getBookmarkView()->getExpandState("active")?1:0; *branches->newChild("range") = wxGetApp().getAppFrame()->getBookmarkView()->getExpandState("range")?1:0; *branches->newChild("bookmark") = wxGetApp().getAppFrame()->getBookmarkView()->getExpandState("bookmark")?1:0; *branches->newChild("recent") = wxGetApp().getAppFrame()->getBookmarkView()->getExpandState("recent")?1:0; DataNode *view_ranges = s.rootNode()->newChild("ranges"); for (auto re_i : ranges) { DataNode *range = view_ranges->newChild("range"); *range->newChild("label") = re_i->label; *range->newChild("freq") = re_i->freq; *range->newChild("start") = re_i->startFreq; *range->newChild("end") = re_i->endFreq; } DataNode *modems = s.rootNode()->newChild("modems"); for (auto &bmd_i : bmData) { DataNode *group = modems->newChild("group"); *group->newChild("@name") = bmd_i.first; *group->newChild("@expanded") = (getExpandState(bmd_i.first)?std::string("true"):std::string("false")); for (auto &bm_i : bmd_i.second ) { //if a matching demodulator exists, use its data instead to be be saved, because output_device could have been //modified by the user. So, save that "live" version instead. auto matchingDemod = wxGetApp().getDemodMgr().getLastDemodulatorWith(bm_i->type, bm_i->label, bm_i->frequency, bm_i->bandwidth); if (matchingDemod != nullptr) { wxGetApp().getDemodMgr().saveInstance(group->newChild("modem"), matchingDemod); } else { group->newChildCloneFrom("modem", bm_i->node); } } } DataNode *recent_modems = s.rootNode()->newChild("recent_modems"); for (auto demod : wxGetApp().getDemodMgr().getDemodulators()) { wxGetApp().getDemodMgr().saveInstance(recent_modems->newChild("modem"),demod); } for (auto &r_i : this->recents) { recent_modems->newChildCloneFrom("modem", r_i->node); } wxFileName saveFile; wxFileName saveFileBackup; if (useFullpath) { saveFile.Assign(bookmarkFn); saveFileBackup.Assign(bookmarkFn + ".backup"); } else { saveFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn); saveFileBackup.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); } if (saveFile.IsDirWritable()) { // Hopefully leave at least a readable backup in case of failure.. if (backup && saveFile.FileExists() && (!saveFileBackup.FileExists() || saveFileBackup.IsFileWritable())) { wxCopyFile(saveFile.GetFullPath(wxPATH_NATIVE).ToStdString(), saveFileBackup.GetFullPath(wxPATH_NATIVE).ToStdString()); } s.SaveToFileXML(saveFile.GetFullPath(wxPATH_NATIVE).ToStdString()); } } bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup, bool useFullpath) { wxFileName loadFile; wxFileName failFile; wxFileName lastLoaded; wxFileName backupFile; if (useFullpath) { loadFile.Assign(bookmarkFn); failFile.Assign(bookmarkFn + ".failedload"); lastLoaded.Assign(bookmarkFn + ".lastloaded"); backupFile.Assign(bookmarkFn + ".backup"); } else { loadFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn); failFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".failedload"); lastLoaded.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".lastloaded"); backupFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); } DataTree s; bool loadStatusOk = true; // File exists but is not readable if (loadFile.FileExists() && !loadFile.IsFileReadable()) { return false; } // New instance of bookmark savefiles if (backup && !loadFile.FileExists() && !lastLoaded.FileExists() && !backupFile.FileExists()) { wxGetApp().getAppFrame()->getBookmarkView()->loadDefaultRanges(); return true; } // Attempt to load file if (!s.LoadFromFileXML(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString())) { return false; } //Check if it is a bookmark file, read the root node. if (s.rootNode()->getName() != "cubicsdr_bookmarks") { return false; } // Clear any active data bmData.clear(); clearRecents(); clearRanges(); bmDataSorted.clear(); if (s.rootNode()->hasAnother("branches")) { DataNode *branches = s.rootNode()->getNext("branches"); int bActive = 1, bRange = 0, bBookmark = 1, bRecent = 1; if (branches->hasAnother("active")) branches->getNext("active")->element()->get(bActive); if (branches->hasAnother("range")) branches->getNext("range")->element()->get(bRange); if (branches->hasAnother("bookmark")) branches->getNext("bookmark")->element()->get(bBookmark); if (branches->hasAnother("recent")) branches->getNext("recent")->element()->get(bRecent); wxGetApp().getAppFrame()->getBookmarkView()->setExpandState("active", bActive?true:false); wxGetApp().getAppFrame()->getBookmarkView()->setExpandState("range", bRange?true:false); wxGetApp().getAppFrame()->getBookmarkView()->setExpandState("bookmark", bBookmark?true:false); wxGetApp().getAppFrame()->getBookmarkView()->setExpandState("recent", bRecent?true:false); } if (s.rootNode()->hasAnother("ranges")) { DataNode *view_ranges = s.rootNode()->getNext("ranges"); while (view_ranges->hasAnother("range")) { DataNode *range = view_ranges->getNext("range"); BookmarkRangeEntryPtr re(new BookmarkRangeEntry); if (range->hasAnother("label")) range->getNext("label")->element()->get(re->label); if (range->hasAnother("freq")) range->getNext("freq")->element()->get(re->freq); if (range->hasAnother("start")) range->getNext("start")->element()->get(re->startFreq); if (range->hasAnother("end")) range->getNext("end")->element()->get(re->endFreq); addRange(re); } } if (s.rootNode()->hasAnother("modems")) { DataNode *modems = s.rootNode()->getNext("modems"); while (modems->hasAnother("group")) { DataNode *group = modems->getNext("group"); std::string expandState = "true"; std::string groupName = "Unnamed"; if (group->hasAnother("@name")) { groupName = group->getNext("@name")->element()->toString(); } if (group->hasAnother("@expanded")) { expandState = group->getNext("@expanded")->element()->toString(); } setExpandState(groupName, (expandState == "true")); while (group->hasAnother("modem")) { DataNode *modem = group->getNext("modem"); BookmarkEntryPtr be = nodeToBookmark(modem); if (be) { addBookmark(groupName.c_str(), be); } else { std::cout << "error loading bookmarked modem.." << std::endl; loadStatusOk = false; } } } } if (s.rootNode()->hasAnother("recent_modems")) { DataNode *recent_modems = s.rootNode()->getNext("recent_modems"); while (recent_modems->hasAnother("modem")) { DataNode *modem = recent_modems->getNext("modem"); BookmarkEntryPtr be = nodeToBookmark(modem); if (be) { addRecent(be); } else { std::cout << "error loading recent modem.." << std::endl; loadStatusOk = false; } } } if (backup) { if (loadStatusOk) { // Loaded OK; keep a copy if (loadFile.IsDirWritable()) { if (loadFile.FileExists() && (!lastLoaded.FileExists() || lastLoaded.IsFileWritable())) { wxCopyFile(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString(), lastLoaded.GetFullPath(wxPATH_NATIVE).ToStdString()); } } } else if (!loadStatusOk) { if (loadFile.IsDirWritable()) { // Load failed; keep a copy of the failed bookmark file for analysis? if (loadFile.FileExists() && (!failFile.FileExists() || failFile.IsFileWritable())) { wxCopyFile(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString(), failFile.GetFullPath(wxPATH_NATIVE).ToStdString()); } } } } return loadStatusOk; } void BookmarkMgr::resetBookmarks() { // Clear any active data bmData.clear(); clearRecents(); clearRanges(); bmDataSorted.clear(); wxGetApp().getAppFrame()->getBookmarkView()->loadDefaultRanges(); } bool BookmarkMgr::hasLastLoad(std::string bookmarkFn) { wxFileName lastLoaded(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".lastloaded"); return lastLoaded.FileExists() && lastLoaded.IsFileReadable(); } bool BookmarkMgr::hasBackup(std::string bookmarkFn) { wxFileName backupFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup"); return backupFile.FileExists() && backupFile.IsFileReadable(); } void BookmarkMgr::addBookmark(std::string group, DemodulatorInstancePtr demod) { std::lock_guard < std::recursive_mutex > lock(busy_lock); //Create a BookmarkEntry from demod data, saving its //characteristics in be->node. BookmarkEntryPtr be = demodToBookmarkEntry(demod); bmData[group].push_back(be); bmDataSorted[group] = false; } void BookmarkMgr::addBookmark(std::string group, BookmarkEntryPtr be) { std::lock_guard < std::recursive_mutex > lock(busy_lock); bmData[group].push_back(be); bmDataSorted[group] = false; } void BookmarkMgr::removeBookmark(std::string group, BookmarkEntryPtr be) { std::lock_guard < std::recursive_mutex > lockData(busy_lock); std::lock_guard < std::mutex > lockEnt(be->busy_lock); if (bmData.find(group) == bmData.end()) { return; } BookmarkList::iterator i = std::find(bmData[group].begin(), bmData[group].end(), be); if (i != bmData[group].end()) { bmData[group].erase(i); } } void BookmarkMgr::removeBookmark(BookmarkEntryPtr be) { std::lock_guard < std::recursive_mutex > lockData(busy_lock); std::lock_guard < std::mutex > lockEnt(be->busy_lock); for (auto &bmd_i : bmData) { BookmarkList::iterator i = std::find(bmd_i.second.begin(), bmd_i.second.end(), be); if (i != bmd_i.second.end()) { bmd_i.second.erase(i); } } } void BookmarkMgr::moveBookmark(BookmarkEntryPtr be, std::string group) { std::lock_guard < std::recursive_mutex > lockData(busy_lock); std::lock_guard < std::mutex > lockEnt(be->busy_lock); for (auto &bmd_i : bmData) { BookmarkList::iterator i = std::find(bmd_i.second.begin(), bmd_i.second.end(), be); if (i != bmd_i.second.end()) { if (bmd_i.first == group) { return; } bmData[group].push_back(*i); bmd_i.second.erase(i); bmDataSorted[group] = false; bmDataSorted[bmd_i.first] = false; return; } } } void BookmarkMgr::addGroup(std::string group) { std::lock_guard < std::recursive_mutex > lock(busy_lock); if (bmData.find(group) == bmData.end()) { BookmarkList dummy = bmData[group]; } } void BookmarkMgr::removeGroup(std::string group) { std::lock_guard < std::recursive_mutex > lock(busy_lock); BookmarkMap::iterator i = bmData.find(group); if (i != bmData.end()) { bmData.erase(group); } } void BookmarkMgr::renameGroup(std::string group, std::string ngroup) { if (group == ngroup) { return; } std::lock_guard < std::recursive_mutex > lock(busy_lock); BookmarkMap::iterator i = bmData.find(group); BookmarkMap::iterator it = bmData.find(ngroup); if (i != bmData.end() && it != bmData.end()) { for (auto ii : bmData[group]) { bmData[ngroup].push_back(ii); } bmData.erase(group); } else if (i != bmData.end()) { bmData[ngroup] = bmData[group]; bmData.erase(group); } } const BookmarkList& BookmarkMgr::getBookmarks(std::string group) { std::lock_guard < std::recursive_mutex > lock(busy_lock); if (bmData.find(group) == bmData.end()) { return emptyResults; } if (!bmDataSorted[group]) { std::sort(bmData[group].begin(), bmData[group].end(), BookmarkEntryCompare()); bmDataSorted[group] = true; } return bmData[group]; } void BookmarkMgr::getGroups(BookmarkNames &arr) { std::lock_guard < std::recursive_mutex > lockData(busy_lock); for (BookmarkMap::iterator i = bmData.begin(); i!= bmData.end(); ++i) { arr.push_back(i->first); } } void BookmarkMgr::getGroups(wxArrayString &arr) { std::lock_guard < std::recursive_mutex > lockData(busy_lock); for (BookmarkMap::iterator i = bmData.begin(); i!= bmData.end(); ++i) { arr.push_back(i->first); } } void BookmarkMgr::setExpandState(std::string groupName, bool state) { expandState[groupName] = state; } bool BookmarkMgr::getExpandState(std::string groupName) { if (expandState.find(groupName) == expandState.end()) { return true; } return expandState[groupName]; } void BookmarkMgr::updateActiveList() { std::lock_guard < std::recursive_mutex > lockData(busy_lock); if (wxGetApp().isShuttingDown()) { return; } BookmarkView *bmv = wxGetApp().getAppFrame()->getBookmarkView(); if (bmv) { bmv->updateActiveList(); } } void BookmarkMgr::updateBookmarks() { std::lock_guard < std::recursive_mutex > lockData(busy_lock); BookmarkView *bmv = wxGetApp().getAppFrame()->getBookmarkView(); if (bmv) { bmv->updateBookmarks(); } } void BookmarkMgr::updateBookmarks(std::string group) { std::lock_guard < std::recursive_mutex > lockData(busy_lock); BookmarkView *bmv = wxGetApp().getAppFrame()->getBookmarkView(); if (bmv) { bmv->updateBookmarks(group); } } void BookmarkMgr::addRecent(DemodulatorInstancePtr demod) { std::lock_guard < std::recursive_mutex > lock(busy_lock); recents.push_back(demodToBookmarkEntry(demod)); trimRecents(); } void BookmarkMgr::addRecent(BookmarkEntryPtr be) { std::lock_guard < std::recursive_mutex > lock(busy_lock); recents.push_back(be); trimRecents(); } void BookmarkMgr::removeRecent(BookmarkEntryPtr be) { std::lock_guard < std::recursive_mutex > lock(busy_lock); BookmarkList::iterator bm_i = std::find(recents.begin(),recents.end(), be); if (bm_i != recents.end()) { recents.erase(bm_i); } } const BookmarkList& BookmarkMgr::getRecents() { std::lock_guard < std::recursive_mutex > lockData(busy_lock); return recents; } void BookmarkMgr::clearRecents() { std::lock_guard < std::recursive_mutex > lock(busy_lock); recents.clear(); } void BookmarkMgr::trimRecents() { if (recents.size() > BOOKMARK_RECENTS_MAX) { recents.erase(recents.begin(), recents.begin()+1); } } void BookmarkMgr::addRange(BookmarkRangeEntryPtr re) { std::lock_guard < std::recursive_mutex > lock(busy_lock); ranges.push_back(re); rangesSorted = false; } void BookmarkMgr::removeRange(BookmarkRangeEntryPtr re) { std::lock_guard < std::recursive_mutex > lock(busy_lock); BookmarkRangeList::iterator re_i = std::find(ranges.begin(), ranges.end(), re); if (re_i != ranges.end()) { ranges.erase(re_i); } } const BookmarkRangeList& BookmarkMgr::getRanges() { std::lock_guard < std::recursive_mutex > lock(busy_lock); if (!rangesSorted) { std::sort(ranges.begin(), ranges.end(), BookmarkRangeEntryCompare()); rangesSorted = true; } return ranges; } void BookmarkMgr::clearRanges() { std::lock_guard < std::recursive_mutex > lock(busy_lock); ranges.clear(); } BookmarkEntryPtr BookmarkMgr::demodToBookmarkEntry(DemodulatorInstancePtr demod) { BookmarkEntryPtr be(new BookmarkEntry); be->bandwidth = demod->getBandwidth(); be->type = demod->getDemodulatorType(); be->label = demod->getDemodulatorUserLabel(); be->frequency = demod->getFrequency(); //fine to do so here, so long nobody overrides be->node, DataNode will be //deleted at last BookmarkEntryPtr be ref. be->node = new DataNode; wxGetApp().getDemodMgr().saveInstance(be->node, demod); return be; } BookmarkEntryPtr BookmarkMgr::nodeToBookmark(DataNode *node) { if (!node->hasAnother("frequency") || !node->hasAnother("type") || !node->hasAnother("bandwidth")) { return nullptr; } BookmarkEntryPtr be(new BookmarkEntry()); node->getNext("frequency")->element()->get(be->frequency); node->getNext("type")->element()->get(be->type); node->getNext("bandwidth")->element()->get(be->bandwidth); if (node->hasAnother("user_label")) { node->getNext("user_label")->element()->get(be->label); } node->rewindAll(); //fine to do so here, so long nobody overrides be->node, DataNode will be //deleted at last BookmarkEntryPtr be ref. //copy data from *node. be->node = new DataNode("node",*node); return be; } std::wstring BookmarkMgr::getBookmarkEntryDisplayName(BookmarkEntryPtr bmEnt) { std::wstring dispName = bmEnt->label; if (dispName == L"") { std::string freqStr = frequencyToStr(bmEnt->frequency) + " " + bmEnt->type; dispName = wxString(freqStr).ToStdWstring(); } return dispName; } std::wstring BookmarkMgr::getActiveDisplayName(DemodulatorInstancePtr demod) { std::wstring activeName = demod->getDemodulatorUserLabel(); if (activeName == L"") { std::string wstr = frequencyToStr(demod->getFrequency()) + " " + demod->getDemodulatorType(); activeName = wxString(wstr).ToStdWstring(); } return activeName; } void BookmarkMgr::removeActive(DemodulatorInstancePtr demod) { std::lock_guard < std::recursive_mutex > lock(busy_lock); if (demod == nullptr) { return; } //Delete demodulator wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, true); wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, false); wxGetApp().removeDemodulator(demod); wxGetApp().getDemodMgr().deleteThread(demod); } CubicSDR-0.2.3/src/BookmarkMgr.h000066400000000000000000000101551322677621400163010ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include "DemodulatorInstance.h" class DataNode; class BookmarkEntry { public: std::mutex busy_lock; std::string type; //maps on the Demod user label. std::wstring label; long long frequency; int bandwidth; DataNode *node; virtual ~BookmarkEntry(); }; class BookmarkRangeEntry { public: BookmarkRangeEntry() : label(L""), freq(0), startFreq(0), endFreq(0) { } BookmarkRangeEntry(std::wstring label, long long freq, long long startFreq, long long endFreq) : label(label), freq(freq), startFreq(startFreq), endFreq(endFreq) { } std::mutex busy_lock; std::wstring label; long long freq; long long startFreq; long long endFreq; }; typedef std::shared_ptr BookmarkEntryPtr; typedef std::shared_ptr BookmarkRangeEntryPtr; struct BookmarkEntryCompare : public std::binary_function { bool operator()(const BookmarkEntryPtr a, BookmarkEntryPtr b) const { return a->frequency < b->frequency; } }; struct BookmarkRangeEntryCompare : public std::binary_function { bool operator()(const BookmarkRangeEntryPtr a, BookmarkRangeEntryPtr b) const { return a->freq < b->freq; } }; typedef std::vector BookmarkList; typedef std::vector BookmarkRangeList; typedef std::map BookmarkMap; typedef std::map BookmarkMapSorted; typedef std::vector BookmarkNames; typedef std::map BookmarkExpandState; class BookmarkMgr { public: BookmarkMgr(); //if useFullpath = false, use the application config dir. //else assume bookmarkFn is a full path and use it for location. void saveToFile(std::string bookmarkFn, bool backup = true, bool useFullpath = false); bool loadFromFile(std::string bookmarkFn, bool backup = true, bool useFullpath = false); void resetBookmarks(); bool hasLastLoad(std::string bookmarkFn); bool hasBackup(std::string bookmarkFn); void addBookmark(std::string group, DemodulatorInstancePtr demod); void addBookmark(std::string group, BookmarkEntryPtr be); void removeBookmark(std::string group, BookmarkEntryPtr be); void removeBookmark(BookmarkEntryPtr be); void moveBookmark(BookmarkEntryPtr be, std::string group); void addGroup(std::string group); void removeGroup(std::string group); void renameGroup(std::string group, std::string ngroup); const BookmarkList& getBookmarks(std::string group); void getGroups(BookmarkNames &arr); void getGroups(wxArrayString &arr); void setExpandState(std::string groupName, bool state); bool getExpandState(std::string groupName); void updateActiveList(); void updateBookmarks(); void updateBookmarks(std::string group); void addRecent(DemodulatorInstancePtr demod); void addRecent(BookmarkEntryPtr be); void removeRecent(BookmarkEntryPtr be); const BookmarkList& getRecents(); void clearRecents(); void removeActive(DemodulatorInstancePtr demod); void addRange(BookmarkRangeEntryPtr re); void removeRange(BookmarkRangeEntryPtr re); const BookmarkRangeList& getRanges(); void clearRanges(); static std::wstring getBookmarkEntryDisplayName(BookmarkEntryPtr bmEnt); static std::wstring getActiveDisplayName(DemodulatorInstancePtr demod); protected: void trimRecents(); BookmarkEntryPtr demodToBookmarkEntry(DemodulatorInstancePtr demod); BookmarkEntryPtr nodeToBookmark(DataNode *node); BookmarkMap bmData; BookmarkMapSorted bmDataSorted; BookmarkList recents; BookmarkRangeList ranges; bool rangesSorted; std::recursive_mutex busy_lock; BookmarkExpandState expandState; //represents an empty BookMarkList that is returned by reference by some functions. static const BookmarkList emptyResults; }; CubicSDR-0.2.3/src/CubicSDR.cpp000066400000000000000000000745341322677621400160320ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #define OPENGL #include "CubicSDRDefs.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include #ifdef _OSX_APP_ #include "CoreFoundation/CoreFoundation.h" #endif #ifdef USE_HAMLIB #include "RigThread.h" #endif IMPLEMENT_APP(CubicSDR) #include #include #include "ActionDialog.h" #include //#ifdef ENABLE_DIGITAL_LAB //// console output buffer for windows //#ifdef _WINDOWS //class outbuf : public std::streambuf { // public: // outbuf() { // setp(0, 0); // } // virtual int_type overflow(int_type c = traits_type::eof()) { // return fputc(c, stdout) == EOF ? traits_type::eof() : c; // } //}; //#endif //#endif #ifdef MINGW_PATCH FILE _iob[] = { *stdin, *stdout, *stderr }; extern "C" FILE * __cdecl __iob_func(void) { return _iob; } extern "C" int __cdecl __isnan(double x) { return _finite(x)?0:1; } extern "C" int __cdecl __isnanf(float x) { return _finitef(x)?0:1; } #endif std::string& filterChars(std::string& s, const std::string& allowed) { s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) { return allowed.find(c) == std::string::npos; }), s.end()); return s; } std::string frequencyToStr(long long freq) { long double freqTemp; freqTemp = freq; std::string suffix(""); std::stringstream freqStr; if (freqTemp >= 1.0e9) { freqTemp /= 1.0e9; freqStr << std::setprecision(10); suffix = std::string("GHz"); } else if (freqTemp >= 1.0e6) { freqTemp /= 1.0e6; freqStr << std::setprecision(7); suffix = std::string("MHz"); } else if (freqTemp >= 1.0e3) { freqTemp /= 1.0e3; freqStr << std::setprecision(4); suffix = std::string("KHz"); } freqStr << freqTemp; freqStr << suffix; return freqStr.str(); } long long strToFrequency(std::string freqStr) { std::string filterStr = filterChars(freqStr, std::string("0123456789.MKGHmkgh")); size_t numLen = filterStr.find_first_not_of("0123456789."); if (numLen == std::string::npos) { numLen = freqStr.length(); } std::string numPartStr = freqStr.substr(0, numLen); std::string suffixStr = freqStr.substr(numLen); std::stringstream numPartStream; numPartStream.str(numPartStr); long double freqTemp = 0; numPartStream >> freqTemp; if (suffixStr.length()) { if (suffixStr.find_first_of("Gg") != std::string::npos) { freqTemp *= 1.0e9; } else if (suffixStr.find_first_of("Mm") != std::string::npos) { freqTemp *= 1.0e6; } else if (suffixStr.find_first_of("Kk") != std::string::npos) { freqTemp *= 1.0e3; } else if (suffixStr.find_first_of("Hh") != std::string::npos) { // ... } } else if (numPartStr.find_first_of(".") != std::string::npos || freqTemp <= 3000) { freqTemp *= 1.0e6; } return (long long) freqTemp; } class ActionDialogBookmarkCatastophe : public ActionDialog { public: ActionDialogBookmarkCatastophe() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Last-Loaded Backup Failure :( :( :(")) { m_questionText->SetLabelText(wxT("All attempts to recover bookmarks have failed. \nWould you like to exit without touching any more save files?\nClick OK to exit without saving; or Cancel to continue anyways.")); } void doClickOK() { wxGetApp().getAppFrame()->disableSave(true); wxGetApp().getAppFrame()->Close(false); } }; class ActionDialogBookmarkBackupLoadFailed : public ActionDialog { public: ActionDialogBookmarkBackupLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Backup Load Failure :( :(")) { m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks backup file. \nWould you like to attempt to load the last succssfully loaded bookmarks file?")); } void doClickOK() { if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { if (wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.lastloaded",false)) { wxGetApp().getBookmarkMgr().updateBookmarks(); wxGetApp().getBookmarkMgr().updateActiveList(); } else { ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); } } } }; class ActionDialogBookmarkLoadFailed : public ActionDialog { public: ActionDialogBookmarkLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Load Failure :(")) { m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks file. \nWould you like to attempt to load the backup file?")); } void doClickOK() { bool loadOk = false; if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) { loadOk = wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.backup",false); } if (loadOk) { wxGetApp().getBookmarkMgr().updateBookmarks(); wxGetApp().getBookmarkMgr().updateActiveList(); } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed()); } else { ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); } } }; CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), agcMode(false) { sampleRateInitialized.store(false); agcMode.store(true); soloMode.store(false); shuttingDown.store(false); fdlgTarget = FrequencyDialog::FDIALOG_TARGET_DEFAULT; stoppedDev = nullptr; } bool CubicSDR::OnInit() { //use the current locale most appropriate to this system, //so that character-related functions are likely to handle Unicode //better (by default, was "C" locale). std::setlocale(LC_ALL, ""); //#ifdef _OSX_APP_ // CFBundleRef mainBundle = CFBundleGetMainBundle(); // CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); // char path[PATH_MAX]; // if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX)) // { // // error! // } // CFRelease(resourcesURL); // chdir(path); //#endif if (!wxApp::OnInit()) { return false; } //Deactivated code to allocate an explicit Console on Windows. //This tends to hang the apllication on heavy demod (re)creation. //To continue to debug with std::cout traces, simply run CubicSDR in a MINSYS2 compatble shell on Windows: //ex: Cygwin shell, Git For Windows Bash shell.... #if (0) if (AllocConsole()) { freopen("CONOUT$", "w", stdout); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); SetConsoleTitle(L"CubicSDR: stdout"); } //refresh ofstream ob; std::streambuf *sb = std::cout.rdbuf(); std::cout.rdbuf(sb); #endif wxApp::SetAppName(CUBICSDR_INSTALL_NAME); #ifdef USE_HAMLIB t_Rig = nullptr; rigThread = nullptr; RigThread::enumerate(); #endif Modem::addModemFactory(ModemFM::factory, "FM", 200000); Modem::addModemFactory(ModemNBFM::factory, "NBFM", 12500); Modem::addModemFactory(ModemFMStereo::factory, "FMS", 200000); Modem::addModemFactory(ModemAM::factory, "AM", 6000); Modem::addModemFactory(ModemLSB::factory, "LSB", 5400); Modem::addModemFactory(ModemUSB::factory, "USB", 5400); Modem::addModemFactory(ModemDSB::factory, "DSB", 5400); Modem::addModemFactory(ModemIQ::factory, "I/Q", 48000); #ifdef ENABLE_DIGITAL_LAB Modem::addModemFactory(ModemAPSK::factory, "APSK", 200000); Modem::addModemFactory(ModemASK::factory, "ASK", 200000); Modem::addModemFactory(ModemBPSK::factory, "BPSK", 200000); Modem::addModemFactory(ModemDPSK::factory, "DPSK", 200000); Modem::addModemFactory(ModemFSK::factory, "FSK", 19200); Modem::addModemFactory(ModemGMSK::factory, "GMSK", 19200); Modem::addModemFactory(ModemOOK::factory, "OOK", 200000); Modem::addModemFactory(ModemPSK::factory, "PSK", 200000); Modem::addModemFactory(ModemQAM::factory, "QAM", 200000); Modem::addModemFactory(ModemQPSK::factory, "QPSK", 200000); Modem::addModemFactory(ModemSQAM::factory, "SQAM", 200000); Modem::addModemFactory(ModemST::factory, "ST", 200000); #endif frequency = wxGetApp().getConfig()->getCenterFreq(); offset = 0; ppm = 0; devicesReady.store(false); devicesFailed.store(false); deviceSelectorOpen.store(false); // Visual Data spectrumVisualThread = new SpectrumVisualDataThread(); pipeIQVisualData = std::make_shared(); pipeIQVisualData->set_max_num_items(1); pipeWaterfallIQVisualData = std::make_shared(); pipeWaterfallIQVisualData->set_max_num_items(128); getSpectrumProcessor()->setInput(pipeIQVisualData); getSpectrumProcessor()->setHideDC(true); // I/Q Data pipeSDRIQData = std::make_shared(); pipeSDRIQData->set_max_num_items(100); sdrThread = new SDRThread(); sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData); sdrPostThread = new SDRPostThread(); sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData); sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData); sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData); #if CUBICSDR_ENABLE_VIEW_SCOPE pipeAudioVisualData = std::make_shared(); pipeAudioVisualData->set_max_num_items(1); scopeProcessor.setInput(pipeAudioVisualData); #else pipeAudioVisualData = nullptr; #endif #if CUBICSDR_ENABLE_VIEW_DEMOD demodVisualThread = new SpectrumVisualDataThread(); pipeDemodIQVisualData = std::make_shared(); pipeDemodIQVisualData->set_max_num_items(1); if (getDemodSpectrumProcessor()) { getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData); } sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData); #else demodVisualThread = nullptr; pipeDemodIQVisualData = nullptr; t_DemodVisual = nullptr; #endif // Now that input/output queue plumbing is completely done, we can //safely starts all the threads: t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread); if (demodVisualThread != nullptr) { t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread); } //Start SDRPostThread last. t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread); sdrEnum = new SDREnumerator(); SDREnumerator::setManuals(config.getManualDevices()); appframe = new AppFrame(); t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); //#ifdef __APPLE__ // int main_policy; // struct sched_param main_param; // // main_policy = SCHED_RR; // main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2; // // pthread_setschedparam(pthread_self(), main_policy, &main_param); //#endif if (!wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml")) { if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) { ActionDialog::showDialog(new ActionDialogBookmarkLoadFailed()); } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed()); } else { ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); } } else { getBookmarkMgr().updateActiveList(); getBookmarkMgr().updateBookmarks(); } return true; } int CubicSDR::OnExit() { shuttingDown.store(true); #if USE_HAMLIB if (rigIsActive()) { std::cout << "Terminating Rig thread.." << std::endl << std::flush; stopRig(); } #endif bool terminationSequenceOK = true; //The thread feeding them all should be terminated first, so: std::cout << "Terminating SDR thread.." << std::endl << std::flush ; sdrThread->terminate(); terminationSequenceOK = terminationSequenceOK && sdrThread->isTerminated(3000); //in case termination sequence goes wrong, kill App brutally now because it can get stuck. if (!terminationSequenceOK) { //no trace here because it could occur if the device is not started. ::exit(11); } std::cout << "Terminating SDR post-processing thread.." << std::endl << std::flush; sdrPostThread->terminate(); //Wait for termination for sdrPostThread second:: since it is doing //mostly blocking push() to the other threads, they must stay alive //so that sdrPostThread can complete a processing loop and die. terminationSequenceOK = terminationSequenceOK && sdrPostThread->isTerminated(3000); //in case termination sequence goes wrong, kill App brutally now because it can get stuck. if (!terminationSequenceOK) { std::cout << "Cannot terminate application properly, calling exit() now." << std::endl << std::flush; ::exit(12); } std::cout << "Terminating All Demodulators.." << std::endl << std::flush; demodMgr.terminateAll(); std::cout << "Terminating Visual Processor threads.." << std::endl << std::flush; spectrumVisualThread->terminate(); if (demodVisualThread) { demodVisualThread->terminate(); } //Wait nicely terminationSequenceOK = terminationSequenceOK && spectrumVisualThread->isTerminated(1000); if (demodVisualThread) { terminationSequenceOK = terminationSequenceOK && demodVisualThread->isTerminated(1000); } //in case termination sequence goes wrong, kill App brutally because it can get stuck. if (!terminationSequenceOK) { std::cout << "Cannot terminate application properly, calling exit() now." << std::endl << std::flush; ::exit(13); } //Then join the thread themselves: if (t_SDR) { t_SDR->join(); } t_PostSDR->join(); if (t_DemodVisual) { t_DemodVisual->join(); } t_SpectrumVisual->join(); //Now only we can delete: delete t_SDR; t_SDR = nullptr; delete sdrThread; sdrThread = nullptr; delete sdrPostThread; sdrPostThread = nullptr; delete t_PostSDR; t_PostSDR = nullptr; delete t_SpectrumVisual; t_SpectrumVisual = nullptr; delete spectrumVisualThread; spectrumVisualThread = nullptr; delete t_DemodVisual; t_DemodVisual = nullptr; delete demodVisualThread; demodVisualThread = nullptr; delete m_glContext; m_glContext = nullptr; std::cout << "Application termination complete." << std::endl << std::flush; #ifdef __APPLE__ AudioThread::deviceCleanup(); #endif return wxApp::OnExit(); } PrimaryGLContext& CubicSDR::GetContext(wxGLCanvas *canvas) { PrimaryGLContext *glContext; if (!m_glContext) { m_glContext = new PrimaryGLContext(canvas, NULL); } glContext = m_glContext; return *glContext; } void CubicSDR::OnInitCmdLine(wxCmdLineParser& parser) { parser.SetDesc (commandLineInfo); parser.SetSwitchChars (wxT("-")); } bool CubicSDR::OnCmdLineParsed(wxCmdLineParser& parser) { wxString *confName = new wxString; if (parser.Found("c",confName)) { if (confName) { config.setConfigName(confName->ToStdString()); } } config.load(); #ifdef BUNDLE_SOAPY_MODS if (parser.Found("b")) { useLocalMod.store(false); } else { useLocalMod.store(true); } #else useLocalMod.store(true); #endif wxString *modPath = new wxString; if (parser.Found("m",modPath)) { if (modPath) { modulePath = modPath->ToStdString(); } else { modulePath = ""; } } return true; } void CubicSDR::closeDeviceSelector() { if (deviceSelectorOpen) { deviceSelectorDialog->Close(); } } void CubicSDR::deviceSelector() { if (deviceSelectorOpen) { deviceSelectorDialog->Raise(); deviceSelectorDialog->SetFocus(); return; } deviceSelectorOpen.store(true); deviceSelectorDialog = new SDRDevicesDialog(appframe); deviceSelectorDialog->Show(); } void CubicSDR::addRemote(std::string remoteAddr) { SDREnumerator::addRemote(remoteAddr); devicesReady.store(false); t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); } void CubicSDR::removeRemote(std::string remoteAddr) { SDREnumerator::removeRemote(remoteAddr); } void CubicSDR::sdrThreadNotify(SDRThread::SDRThreadState state, std::string message) { std::lock_guard < std::mutex > lock(notify_busy); if (state == SDRThread::SDR_THREAD_INITIALIZED) { appframe->initDeviceParams(getDevice()); } if (state == SDRThread::SDR_THREAD_MESSAGE) { notifyMessage = message; } if (state == SDRThread::SDR_THREAD_FAILED) { notifyMessage = message; // wxMessageDialog *info; // info = new wxMessageDialog(NULL, message, wxT("Error initializing device"), wxOK | wxICON_ERROR); // info->ShowModal(); } //if (appframe) { appframe->SetStatusText(message); } } void CubicSDR::sdrEnumThreadNotify(SDREnumerator::SDREnumState state, std::string message) { std::lock_guard < std::mutex > lock(notify_busy); if (state == SDREnumerator::SDR_ENUM_MESSAGE) { notifyMessage = message; } if (state == SDREnumerator::SDR_ENUM_DEVICES_READY) { devs = SDREnumerator::enumerate_devices("", true); devicesReady.store(true); } if (state == SDREnumerator::SDR_ENUM_FAILED) { devicesFailed.store(true); } //if (appframe) { appframe->SetStatusText(message); } } void CubicSDR::setFrequency(long long freq) { if (freq < sampleRate / 2) { freq = sampleRate / 2; } frequency = freq; sdrThread->setFrequency(freq); getSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold()); //make the peak hold act on the current dmod also, like a zoomed-in version. if (getDemodSpectrumProcessor()) { getDemodSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold()); } } long long CubicSDR::getOffset() { return offset; } void CubicSDR::setOffset(long long ofs) { offset = ofs; if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setOffset(offset); } } void CubicSDR::setAntennaName(const std::string& name) { antennaName = name; if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setAntenna(antennaName); } } const std::string& CubicSDR::getAntennaName() { return antennaName; } long long CubicSDR::getFrequency() { return frequency; } void CubicSDR::lockFrequency(long long freq) { frequency_locked.store(true); lock_freq.store(freq); if (sdrThread && !sdrThread->isTerminated()) { sdrThread->lockFrequency(freq); } } bool CubicSDR::isFrequencyLocked() { return frequency_locked.load(); } void CubicSDR::unlockFrequency() { frequency_locked.store(false); if (sdrThread && !sdrThread->isTerminated()) { sdrThread->unlockFrequency(); } } void CubicSDR::setSampleRate(long long rate_in) { sampleRate = rate_in; if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setSampleRate(sampleRate); } setFrequency(frequency); if (rate_in <= CHANNELIZER_RATE_MAX / 8) { appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE / 4); appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false); spectrumVisualThread->getProcessor()->setHideDC(false); } else if (rate_in <= CHANNELIZER_RATE_MAX) { appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE / 2); appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false); spectrumVisualThread->getProcessor()->setHideDC(false); } else if (rate_in > CHANNELIZER_RATE_MAX) { appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE); appframe->getWaterfallDataThread()->getProcessor()->setHideDC(true); spectrumVisualThread->getProcessor()->setHideDC(true); } } void CubicSDR::stopDevice(bool store, int waitMsForTermination) { //Firt we must stop the threads sdrThread->terminate(); sdrThread->isTerminated(waitMsForTermination); if (t_SDR) { t_SDR->join(); delete t_SDR; t_SDR = nullptr; } //Only now we can nullify devices if (store) { stoppedDev = sdrThread->getDevice(); } else { stoppedDev = nullptr; } sdrThread->setDevice(nullptr); } void CubicSDR::reEnumerateDevices() { devicesReady.store(false); devs = nullptr; SDREnumerator::reset(); t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); } void CubicSDR::setDevice(SDRDeviceInfo *dev, int waitMsForTermination) { sdrThread->terminate(); sdrThread->isTerminated(waitMsForTermination); if (t_SDR) { t_SDR->join(); delete t_SDR; t_SDR = nullptr; } for (SoapySDR::Kwargs::const_iterator i = settingArgs.begin(); i != settingArgs.end(); i++) { sdrThread->writeSetting(i->first, i->second); } sdrThread->setStreamArgs(streamArgs); sdrThread->setDevice(dev); DeviceConfig *devConfig = config.getDevice(dev->getDeviceId()); SoapySDR::Device *soapyDev = dev->getSoapyDevice(); if (soapyDev) { if (long devSampleRate = devConfig->getSampleRate()) { sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, devSampleRate); sampleRateInitialized.store(true); } if (!sampleRateInitialized.load()) { sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, DEFAULT_SAMPLE_RATE); sampleRateInitialized.store(true); } else { sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, sampleRate); } if (frequency < sampleRate/2) { frequency = sampleRate/2; } setFrequency(frequency); setSampleRate(sampleRate); setPPM(devConfig->getPPM()); setOffset(devConfig->getOffset()); setAGCMode(devConfig->getAGCMode()); setAntennaName(devConfig->getAntennaName()); t_SDR = new std::thread(&SDRThread::threadMain, sdrThread); } stoppedDev = nullptr; } SDRDeviceInfo *CubicSDR::getDevice() { if (!sdrThread->getDevice() && stoppedDev) { return stoppedDev; } return sdrThread->getDevice(); } ScopeVisualProcessor *CubicSDR::getScopeProcessor() { return &scopeProcessor; } SpectrumVisualProcessor *CubicSDR::getSpectrumProcessor() { return spectrumVisualThread->getProcessor(); } SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() { if (demodVisualThread) { return demodVisualThread->getProcessor(); } else { return nullptr; } } DemodulatorThreadOutputQueuePtr CubicSDR::getAudioVisualQueue() { return pipeAudioVisualData; } DemodulatorThreadInputQueuePtr CubicSDR::getIQVisualQueue() { return pipeIQVisualData; } DemodulatorThreadInputQueuePtr CubicSDR::getWaterfallVisualQueue() { return pipeWaterfallIQVisualData; } DemodulatorMgr &CubicSDR::getDemodMgr() { return demodMgr; } BookmarkMgr &CubicSDR::getBookmarkMgr() { return bookmarkMgr; } SDRPostThread *CubicSDR::getSDRPostThread() { return sdrPostThread; } SDRThread *CubicSDR::getSDRThread() { return sdrThread; } void CubicSDR::notifyDemodulatorsChanged() { sdrPostThread->notifyDemodulatorsChanged(); } long long CubicSDR::getSampleRate() { return sampleRate; } void CubicSDR::removeDemodulator(DemodulatorInstancePtr demod) { if (!demod) { return; } demod->setActive(false); sdrPostThread->notifyDemodulatorsChanged(); wxGetApp().getAppFrame()->notifyUpdateModemProperties(); } std::vector* CubicSDR::getDevices() { return devs; } AppConfig *CubicSDR::getConfig() { return &config; } void CubicSDR::saveConfig() { config.save(); } void CubicSDR::setPPM(int ppm_in) { ppm = ppm_in; if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setPPM(ppm); } } int CubicSDR::getPPM() { SDRDeviceInfo *dev = sdrThread->getDevice(); if (dev) { ppm = config.getDevice(dev->getDeviceId())->getPPM(); } return ppm; } void CubicSDR::showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode, wxString initString) { const wxString demodTitle("Set Demodulator Frequency"); const wxString freqTitle("Set Center Frequency"); const wxString bwTitle("Modem Bandwidth (150Hz - 500KHz)"); const wxString lpsTitle("Lines-Per-Second (1-1024)"); const wxString avgTitle("Average Rate (0.1 - 0.99)"); const wxString gainTitle("Gain Entry: "+wxGetApp().getActiveGainEntry()); wxString title; switch (targetMode) { case FrequencyDialog::FDIALOG_TARGET_DEFAULT: case FrequencyDialog::FDIALOG_TARGET_FREQ: title = demodMgr.getActiveDemodulator()?demodTitle:freqTitle; break; case FrequencyDialog::FDIALOG_TARGET_BANDWIDTH: title = bwTitle; break; case FrequencyDialog::FDIALOG_TARGET_WATERFALL_LPS: title = lpsTitle; break; case FrequencyDialog::FDIALOG_TARGET_SPECTRUM_AVG: title = avgTitle; break; case FrequencyDialog::FDIALOG_TARGET_GAIN: title = gainTitle; if (wxGetApp().getActiveGainEntry() == "") { return; } break; default: break; } FrequencyDialog fdialog(appframe, -1, title, demodMgr.getActiveDemodulator(), wxPoint(-100,-100), wxSize(350, 75), wxDEFAULT_DIALOG_STYLE, targetMode, initString); fdialog.ShowModal(); } void CubicSDR::showLabelInput() { DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); if (activeDemod != nullptr) { const wxString demodTitle("Edit Demodulator label"); DemodLabelDialog labelDialog(appframe, -1, demodTitle, activeDemod, wxPoint(-100, -100), wxSize(500, 75), wxDEFAULT_DIALOG_STYLE); labelDialog.ShowModal(); } } AppFrame *CubicSDR::getAppFrame() { return appframe; } void CubicSDR::setFrequencySnap(int snap) { if (snap > 1000000) { snap = 1000000; } this->snap = snap; } int CubicSDR::getFrequencySnap() { return snap; } bool CubicSDR::areDevicesReady() { return devicesReady.load(); } void CubicSDR::notifyMainUIOfDeviceChange(bool forceRefreshOfGains) { appframe->notifyDeviceChanged(); if (forceRefreshOfGains) { appframe->refreshGainUI(); } } bool CubicSDR::areDevicesEnumerating() { return !sdrEnum->isTerminated(); } bool CubicSDR::areModulesMissing() { return devicesFailed.load(); } std::string CubicSDR::getNotification() { std::string msg; std::lock_guard < std::mutex > lock(notify_busy); msg = notifyMessage; return msg; } void CubicSDR::setDeviceSelectorClosed() { deviceSelectorOpen.store(false); } bool CubicSDR::isDeviceSelectorOpen() { return deviceSelectorOpen.load(); } void CubicSDR::setAGCMode(bool mode) { agcMode.store(mode); if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setAGCMode(mode); } } bool CubicSDR::getAGCMode() { return agcMode.load(); } void CubicSDR::setGain(std::string name, float gain_in) { sdrThread->setGain(name,gain_in); } float CubicSDR::getGain(std::string name) { return sdrThread->getGain(name); } void CubicSDR::setStreamArgs(SoapySDR::Kwargs streamArgs_in) { streamArgs = streamArgs_in; } void CubicSDR::setDeviceArgs(SoapySDR::Kwargs settingArgs_in) { settingArgs = settingArgs_in; } bool CubicSDR::getUseLocalMod() { return useLocalMod.load(); } std::string CubicSDR::getModulePath() { return modulePath; } void CubicSDR::setActiveGainEntry(std::string gainName) { activeGain = gainName; } std::string CubicSDR::getActiveGainEntry() { return activeGain; } void CubicSDR::setSoloMode(bool solo) { soloMode.store(solo); } bool CubicSDR::getSoloMode() { return soloMode.load(); } bool CubicSDR::isShuttingDown() { return shuttingDown.load(); } int CubicSDR::FilterEvent(wxEvent& event) { if (!appframe) { return -1; } if (event.GetEventType() == wxEVT_KEY_DOWN || event.GetEventType() == wxEVT_CHAR_HOOK) { return appframe->OnGlobalKeyDown((wxKeyEvent&)event); } if (event.GetEventType() == wxEVT_KEY_UP || event.GetEventType() == wxEVT_CHAR_HOOK) { return appframe->OnGlobalKeyUp((wxKeyEvent&)event); } return -1; // process normally } #ifdef USE_HAMLIB RigThread *CubicSDR::getRigThread() { return rigThread; } void CubicSDR::initRig(int rigModel, std::string rigPort, int rigSerialRate) { if (rigThread) { rigThread->terminate(); rigThread->isTerminated(1000); } if (t_Rig && t_Rig->joinable()) { t_Rig->join(); } //now we can delete if (rigThread) { delete rigThread; rigThread = nullptr; } if (t_Rig) { delete t_Rig; t_Rig = nullptr; } rigThread = new RigThread(); rigThread->initRig(rigModel, rigPort, rigSerialRate); rigThread->setControlMode(wxGetApp().getConfig()->getRigControlMode()); rigThread->setFollowMode(wxGetApp().getConfig()->getRigFollowMode()); rigThread->setCenterLock(wxGetApp().getConfig()->getRigCenterLock()); rigThread->setFollowModem(wxGetApp().getConfig()->getRigFollowModem()); t_Rig = new std::thread(&RigThread::threadMain, rigThread); } void CubicSDR::stopRig() { if (!rigThread) { return; } if (rigThread) { rigThread->terminate(); rigThread->isTerminated(1000); } if (t_Rig && t_Rig->joinable()) { t_Rig->join(); } //now we can delete if (rigThread) { delete rigThread; rigThread = nullptr; } if (t_Rig) { delete t_Rig; t_Rig = nullptr; } } bool CubicSDR::rigIsActive() { return (rigThread && !rigThread->isTerminated()); } #endif CubicSDR-0.2.3/src/CubicSDR.h000066400000000000000000000163771322677621400155000ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once //WX_GL_CORE_PROFILE 1 //WX_GL_MAJOR_VERSION 3 //WX_GL_MINOR_VERSION 2 #include #include "GLExt.h" #include "PrimaryGLContext.h" #include "ThreadBlockingQueue.h" #include "SoapySDRThread.h" #include "SDREnumerator.h" #include "SDRPostThread.h" #include "AudioThread.h" #include "DemodulatorMgr.h" #include "AppConfig.h" #include "AppFrame.h" #include "FrequencyDialog.h" #include "DemodLabelDialog.h" #include "BookmarkMgr.h" #include "ScopeVisualProcessor.h" #include "SpectrumVisualProcessor.h" #include "SpectrumVisualDataThread.h" #include "SDRDevices.h" #include "Modem.h" #include "ModemFM.h" #include "ModemNBFM.h" #include "ModemFMStereo.h" #include "ModemAM.h" #include "ModemUSB.h" #include "ModemLSB.h" #include "ModemDSB.h" #include "ModemIQ.h" #ifdef ENABLE_DIGITAL_LAB #include "ModemAPSK.h" #include "ModemASK.h" #include "ModemBPSK.h" #include "ModemDPSK.h" #include "ModemFSK.h" #include "ModemGMSK.h" #include "ModemOOK.h" #include "ModemPSK.h" #include "ModemQAM.h" #include "ModemQPSK.h" #include "ModemSQAM.h" #include "ModemST.h" #endif #ifdef USE_HAMLIB class RigThread; #endif #include #define NUM_DEMODULATORS 1 std::string& filterChars(std::string& s, const std::string& allowed); std::string frequencyToStr(long long freq); long long strToFrequency(std::string freqStr); class CubicSDR: public wxApp { public: CubicSDR(); PrimaryGLContext &GetContext(wxGLCanvas *canvas); virtual bool OnInit(); virtual int OnExit(); virtual void OnInitCmdLine(wxCmdLineParser& parser); virtual bool OnCmdLineParsed(wxCmdLineParser& parser); void deviceSelector(); void sdrThreadNotify(SDRThread::SDRThreadState state, std::string message); void sdrEnumThreadNotify(SDREnumerator::SDREnumState state, std::string message); void setFrequency(long long freq); long long getFrequency(); void lockFrequency(long long freq); bool isFrequencyLocked(); void unlockFrequency(); void setOffset(long long ofs); long long getOffset(); void setAntennaName(const std::string& name); const std::string& getAntennaName(); void setDBOffset(int ofs); int getDBOffset(); void setSampleRate(long long rate_in); long long getSampleRate(); std::vector *getDevices(); void setDevice(SDRDeviceInfo *dev, int waitMsForTermination); void stopDevice(bool store, int waitMsForTermination); SDRDeviceInfo * getDevice(); ScopeVisualProcessor *getScopeProcessor(); SpectrumVisualProcessor *getSpectrumProcessor(); SpectrumVisualProcessor *getDemodSpectrumProcessor(); DemodulatorThreadOutputQueuePtr getAudioVisualQueue(); DemodulatorThreadInputQueuePtr getIQVisualQueue(); DemodulatorThreadInputQueuePtr getWaterfallVisualQueue(); DemodulatorThreadInputQueuePtr getActiveDemodVisualQueue(); DemodulatorMgr &getDemodMgr(); BookmarkMgr &getBookmarkMgr(); SDRPostThread *getSDRPostThread(); SDRThread *getSDRThread(); void notifyDemodulatorsChanged(); void removeDemodulator(DemodulatorInstancePtr demod); void setFrequencySnap(int snap); int getFrequencySnap(); AppConfig *getConfig(); void saveConfig(); void setPPM(int ppm_in); int getPPM(); void showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode = FrequencyDialog::FDIALOG_TARGET_DEFAULT, wxString initString = ""); void showLabelInput(); AppFrame *getAppFrame(); bool areDevicesReady(); bool areDevicesEnumerating(); bool areModulesMissing(); std::string getNotification(); void notifyMainUIOfDeviceChange(bool forceRefreshOfGains = false); void addRemote(std::string remoteAddr); void removeRemote(std::string remoteAddr); void setDeviceSelectorClosed(); void reEnumerateDevices(); bool isDeviceSelectorOpen(); void closeDeviceSelector(); void setAGCMode(bool mode); bool getAGCMode(); void setGain(std::string name, float gain_in); float getGain(std::string name); void setStreamArgs(SoapySDR::Kwargs streamArgs_in); void setDeviceArgs(SoapySDR::Kwargs settingArgs_in); bool getUseLocalMod(); std::string getModulePath(); void setActiveGainEntry(std::string gainName); std::string getActiveGainEntry(); void setSoloMode(bool solo); bool getSoloMode(); bool isShuttingDown(); #ifdef USE_HAMLIB RigThread *getRigThread(); void initRig(int rigModel, std::string rigPort, int rigSerialRate); void stopRig(); bool rigIsActive(); #endif private: int FilterEvent(wxEvent& event); AppFrame *appframe = nullptr; AppConfig config; PrimaryGLContext *m_glContext = nullptr; std::vector *devs = nullptr; DemodulatorMgr demodMgr; BookmarkMgr bookmarkMgr; std::atomic_llong frequency; std::atomic_llong offset; std::atomic_int ppm, snap; std::atomic_llong sampleRate; std::string antennaName; std::atomic_bool agcMode; std::atomic_bool shuttingDown; SDRThread *sdrThread = nullptr; SDREnumerator *sdrEnum = nullptr; SDRPostThread *sdrPostThread = nullptr; SpectrumVisualDataThread *spectrumVisualThread = nullptr; SpectrumVisualDataThread *demodVisualThread = nullptr; SDRThreadIQDataQueuePtr pipeSDRIQData; DemodulatorThreadInputQueuePtr pipeIQVisualData; DemodulatorThreadOutputQueuePtr pipeAudioVisualData; DemodulatorThreadInputQueuePtr pipeDemodIQVisualData; DemodulatorThreadInputQueuePtr pipeWaterfallIQVisualData; DemodulatorThreadInputQueuePtr pipeActiveDemodIQVisualData; ScopeVisualProcessor scopeProcessor; SDRDevicesDialog *deviceSelectorDialog = nullptr; SoapySDR::Kwargs streamArgs; SoapySDR::Kwargs settingArgs; std::thread *t_SDR = nullptr; std::thread *t_SDREnum = nullptr; std::thread *t_PostSDR = nullptr; std::thread *t_SpectrumVisual = nullptr; std::thread *t_DemodVisual = nullptr; std::atomic_bool devicesReady; std::atomic_bool devicesFailed; std::atomic_bool deviceSelectorOpen; std::atomic_bool sampleRateInitialized; std::atomic_bool useLocalMod; std::string notifyMessage; std::string modulePath; std::mutex notify_busy; std::atomic_bool frequency_locked; std::atomic_llong lock_freq; FrequencyDialog::FrequencyDialogTarget fdlgTarget; std::string activeGain; std::atomic_bool soloMode; SDRDeviceInfo *stoppedDev; #ifdef USE_HAMLIB RigThread* rigThread = nullptr; std::thread *t_Rig = nullptr; #endif }; static const wxCmdLineEntryDesc commandLineInfo [] = { { wxCMD_LINE_SWITCH, "h", "help", "Command line parameter help", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP }, { wxCMD_LINE_OPTION, "c", "config", "Specify a named configuration to use, i.e. '-c ham'", wxCMD_LINE_VAL_STRING, 0 }, { wxCMD_LINE_OPTION, "m", "modpath", "Load modules from suppplied path, i.e. '-m ~/SoapyMods/'", wxCMD_LINE_VAL_STRING, 0 }, #ifdef BUNDLE_SOAPY_MODS { wxCMD_LINE_SWITCH, "b", "bundled", "Use bundled SoapySDR modules first instead of local.", wxCMD_LINE_VAL_NONE, 0 }, #endif { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 } }; DECLARE_APP(CubicSDR) CubicSDR-0.2.3/src/CubicSDR.png000066400000000000000000001771541322677621400160360ustar00rootroot00000000000000‰PNG  IHDR\r¨f pHYs  šœ IDATxÚì½i°$×ußùË=«^moé½¡ÑØD¸‚$¸‰¤$J”µc‡,KòˆÛ3ò|pÈ’øÏþËå³_Ž¥x#ì‡þ^&?*ŽU•ÁR¼¹‡¾¼o\ËÕ*ÛåX*€åxk°[Š¢XÞ¥¥XŽ7 à«û–rÌZÞžå(†¹¼oÌ]G1DKe°K°DK.`©–ã î,Çr,ËÐxdJ$ÀZà"ØË0àR,Ç_ Xá·Xæ,ÇR¼i”À܃_ ÿr° ¾9´ügÿ¥öô'ßC Ì+Ká_Ž¥x3Ž]ó %ÉòF,ÇR¼ÙÆEsÄ…µÕåXŽÒX†ßãüƒŸÎ&4Â%ø_Ž%x“q“]Ú ¯±Â¦±|ä˱Toª±gÅeìù˱KàÍ8ú¦ÍVÓåÊ®Ïhy;–c‰Þ\#JöN}²Ö étÊw½ïÝËàåX*€7˰ ƒ­¦ËÞ,®÷Ö—7e9– àÍ2VãPîLQ4XÞ”åX*€7 'C¶4ƒNœbŽ{Ë›²KðfB©Ñeo– 7›Äí%XŽ¥xS`ì±¥´×–7d9€e5àjüÊ'?•ÏàÖ=ÄÛ;˜ë3ÇN¥©Ü¹˜ ?ý>³œK°ÿ5 ¼½o•p³~1nÖuŽjù#îÅ{˜WJÿ&ËjÁñ,ßžcþùïÿÞrŽ,Àrüÿeüì_ý«Ùá.ç®Ø—¤l:û’tNø©êFUp6Ž™&1w8.gã€#¦¹DK°ãG¾ï{³îM‡è  7q[*u%p£  Î x.ðiy¢è4‰åñÕf“þtÊÿù¹Ï-çÎR,Çk=~æïþݬÇ£ro·ržPª0×פϯ*aùU. *ü€TB lm§4× ¥åÊe©– `9^£ñ·þÖßÊVM“ÑÎŒ´Óz=â4em4"` ‡²µ5´ö%)ö¾¼ÞÿÐå~éZ TGú áКÄL²ü!ô“,e'‰Y+Äå(&Žó,Ã$ަSì±åÜZ*€å¸Þx×{ß›™¦‰aZ$q„Ûhñö›Ðc‚ÄÇ1\ÖF#v:L]' èÙÚ±ÖgºÑY[cÿ•«¥k/RuÿlsÄ4Ù4tÎÛ6{ƶ⽦Í4‰™:8• !Ô ì4ú$&Ñ-lC'‰ ËÉ'—¡“).ŠÄqBç=‰ã8æÏÿìÏ–sp©þë\Ýlb>ºæ`šqœ`š¶Ž!šžÇà³4Á±lÂ$!K“’€¸®ËZ7÷mkLhì! ó4ÞȲ°¢ˆ¶®35M’0¤]XöpêBûû¯\•J Üìcï[åljpDO8wuHä×w›þtÊj³ÉyÛ¦†Œm›8Êß×=Š uƒÝÌ!‹G¸C¿Ëm˜‚aZi.àQf¢ù3Ò¨tO€ihLÐÐ6Qà³¢ôÄ,Ëy‡ÀÏÝ‘%ªX*€oÛxï>fÞ(Û4™ÅÔ)Y¯ÒRØMRüâ}7I¥&¶•’%)š¡ã&)žò¿a’¢i6–“D„°~ÛÐiÍ.Žábê:†mËÿ75›8 KßÇÔlìhš_ßjÊýêÊሞp65J¯ÿ"Öè dkkô ¶¿m™ngLWlš»³Ï¼’e˜º†øŒ“Œ0LÒ’°›“m̵½4¯pOv±ŸDAù;¦`Z+…  ZIQˆç!î»[Ü7¯@añû£ ]¢Š7ªx߇ÈtÍ™\È-c›¸IJ`Y؆AX4À´ CÆÜòR`… W…֮鞥&–_ó{ª0 ›& ÒPÎ󈼶w/ëÍ.qbj6–£aåߨ›³îþi££$Á2 ¢â·Z†Átc9³G«Mv¥¢X¤HÆi*Ÿàâb»Å$þ”Ô´H DbEß§e9L¢€(3±´ØÅm‹2“}& uƒD9'Ñ-öhW2®¡—ƒa9R1¦I†¥å÷:Ñ-yL ƒ†ã,|Nbœ;wVîoseáF#vÄÍË›ÚR¼Îãýßù‘,Õl#FXaÇ6¥…-ù°…»Þî¤d…Õó”N3] “Ç6 #ýºÂ*„ݱM’ØÂ0#)”QaÙU‹ [i•„ÙSPC[dñÓmà&)Ãâúâ:ªàwÓ„±ãrôàALͦÙ4ÑM‹ñn‡=ë3a5]wæ*`2ñ'˜®Kìûø¾O×ße´¶‡ÎÎÆÒ0WºiákvZ6v% QI%!ö£ ›S‚O‹m…Äi†æûs=L!ŽÖ<¿› úJ?cÔŽ±u¤²ÊaÕ6˜DAI‰¬Ú;A@Ãq¤Ò¤+!S˜¤R·C(‰À÷q\'öc¦®áÅ6 3Ä´r×&5-lM#Ì2Ò0Äuü À/~«PDž7ä+>¬-À+ŸúÑͶ†ÎÂN¾çI¡•\‰e× ¨êOËÉƵÇÕQ½¶8OWÿO ‚êÿÔY}qž ùÓÌ‘ ¤z|E 0ò펓ü¾hûöï§¡A·›WêíY³éOuºívY ÇN»ž ñ¦¹›à´sU4‰cÏ+†ïã ¶q6 ^ÚÜKwu$•Cé7V„çç‚*8ˆÝhJÍ@˜$8QĤ°ÚŠ ’ïh»ÅN‘?°f˜r_ö}fAXsì4‘ˆ@(‰V¢¼„H´Ð-žá$ 0ÝFå÷{„5ÓËÖg K É8NhÚ†D1*JÇH#ÉuxÞpvÿ‚ü¼ÿÚÜíµzUÐ}?˜ÓÜbT­´*Œa¤c[éœpª>¹ªD긺/,¼8W ៪‚.ÞS¿—ê&TI£æ^x²+N2v•ÏHÀu]\Çaµ×£³Ò¤Ùj³¾gOnùÛÚ¦NËq˜³{™EC&Z®ZÙ˜Á¤¾ÉO0öð‰¥2¸²m³g=”V.æ{ªÂ§)»QŒ–Ä%áp¢¯"]å™\É4öhCÝ ›&„ºW ž°@pÃB¹«÷^(|õºª1è:váV%…ÐǾ‡é6¸4¿ª®5œÄBá(žqõšê1ñº:?KÏ@ù1L‹?ù£?ÔÞ0 à?üÙÛhHÁ_4Tø®úâ‹,í"Ÿ[%ÖTkœÄVI¨ëJ–¤ò}ݲJÄžJðUÏU•‚àêÿeIJ»`¶Ý$•‚¯*…¶rL¼/ŽgŽË± v:z½.^GIzë´ÜÖl"¶ ï±@ ÁØ£Ñ ñ¦6>1±ïÓ1l%«tûWº+%.ApSÓ¤njӔ8MYå“üŠ=¡Î\„ qÌJËï/,äša–„}'‰1,§DôÃ×6Jç¨:TŸP Õ­xAãØfik¤‘´âUã£ò‹®]w¾pƒ0&‰#ñ™ãâHº¸êÿ+ö³$•sP=÷åƒ~ä'þfÅi¢ìµ õ묵PAá8iÍ kédY(µlõuÊ‹‡¯"U)T-÷"å  ¨6ÚIl±R\[õíÀ¨P^¨°ß/¾—p vu“•4¦g»ô58à‡¼dè4-“Ù>yBÈ XãÄF"C{ÕñᤀµÛ;œJS™ð ‘?‹C—ûܺgƒ„C—û¥¼1Ô6â"dxYKÙŸé\ÖR:qþ{w4Ìu%²üFj™2j ÆP^U!Ô |•P­¾§Zÿ:k]§T!¬C¦’X.xU˜ ¶ÊA¨ûU… ø‰Y.D"Imõ3šÍͼÖüƒö­ZýÞê*I<î¨â[ÖE(A(ƒ*G0gýE \!ÌÂuP•AI\ËMXÄ¥åûAIUQC±òb B‹Â­,‹…u½dÛC†+MV ðЭ¹j€¨<^ñeŠ4áEµU „ÿ’kcL&¤VùEpAÓæÂU?~ѱEQ˜°¢0ª0]<ëØ÷HtK>ŸØŸ]g¥ q× ³ çÅ1/Èh8ÚœÀ/ú¿E×Ü„8?Œ¦¯‰"xÕ|߇ÈŽÝzÁîWa¨ç> J$à*ƒ:סŽXÄh†>Pß»éW=W½^õ³5ÝèE¨.ˆêˆë©Ö/(  ¦Üdš BŸ8ÉØh4„>8¥i˜L“½Ù”Å@j% ,®¨ þM;ýR=€ªªŠ`ÓÐ¥ð‹¢ ½Ù”ßKXá“ ´3ŒmV³)¡nH¢°ÏÂX’~-Ë™nõM”ü „ dU˜«ÜÀõ¬|Õ_W…\}½H)„‰IÃÑ|ŸTshFÍ 2lCðf鸞ˆk«ç«Ÿ÷íD¯ú"ŸúÑÍ\וڭV¸£ ×Òd(©NX¦UB ¾ç±ˆK¸BP…³êcÕ ¶jáUQu ®EîáG Ú†!a°ðíT¨'™„ÿ‚«¨ŽÝ"j°–eèÍ&étŠÞl–ι­~D ÿ”W‹T% –W‡P“,eì¸8Q„&ôµ&]3Dâ9ÂOu¾_F‘ ½ ÷Åk?LJa@!´"Oà•XíêH“ ÝÐJ¬n¯õÿÕs…õ-†P,¶Kåñíà ^UK°ïûÁ( ¿[ÙŠ}×Ê¿ŸSœk7»t{=\×¥ÛëѰ5 Ó(ÿ_£!·¶aÈ׎£ÉךnHAÓ GCÓ ÂHDZMéhÃ,¶A¥3æ¶êB¨‚/…دÅöý€4›MX'Š¢P^+ò þ š´r]3?Ï44v3‡µB(…ð î!N™:ýâQ»Aˆ„œc‚ ”Ö¹Nø…Ï_'ð‹Æñ§N“XV¦VÎWì¿=N=Æ·‘DA©NÀ+\ ï“è–$çÛ”B?‰)ì‚ù÷‚'ü€z1ê„_÷QM»fc:áŸ&1Ï~ñ7•åÀ¡n G1Wq臉Tœ–ž[vá“‹ k˜–´îQV„ÏtKúÝ2ÛNÉqÈ­qYøíœ©U«\'ܪ¥n8Μu–W ÉÙÏιQ ­ ª˜YšàH¨¢é¹ 9E¨TÓ \·l@Ô×¥Ïtæ Ž8Vkb¾Uy(ÛÊ÷£Tøû³ù•#«%ˆãËyõvüC‡·¿ãÙ½ï~7ÃÁg¥[‚î‡ӉDz“ê&MÇ*‘|‹Æ4ˆh:Ó ’JdúWP”¡%$ÚÈCÁ¨ºj¾B]îÂ"ˆ¦i6YÊ­š-&&Ÿ€„Õbµ<¶ZE×UØu;MJ9„Pç¿  ":‹¯*µÈå(f¿eJ_@öj¼[øíuœzîõ v•@«ËU…X„‚]×Á÷\×) Ú¢èQ5ÿDEê¼P_בÒ7’ô&>G {ÍœRç†jL‘˜â>Š ¤Y𪫠_10l?*rØ“§¤Âé»ÙÂÛ‚ð½¸Ó ¡(6Z&ÓÈšS ¾ça7[R9¤º Ó X(Äš)¡v/Ì~ßójÝAøU}¹Ùë´¸•©¼¥Â'Ì’x6™K6ž‹R ‹4W5l¶“ĬÐ[ZdE ¨¾z…ÛµyŽÿnßaÙí÷Eo‹ƒöMÃä›W/ÉõE§[Ï•€ajäyùÃ$ÍóàõYl=IRŒE¤›n-$ÑT6ÒˆnÓ)åGäVX/=Õòæ–Õ‘Ð|‘Ð6VZ%.F·Q6꾺­õüâ  –?™s#s·#^¦œ)ÇÑèٜᄆþêÖ{|Å   7´$ H(…Ã餴U‰Áª–­ãôÄcir?5fç­u\¦‘Ì&”žºóÜß×$Bp\—À÷K®C'%‚Qý>s_±0U~¡)Ì¡œXÔ*“[}à"…Ö.bájúªh»%båvšH% xiÅM“?¾|ŽïÞxîû|÷þÃü«Ë礠»Î:;ÀÖVÈúÚº,Ö<‡ß4Í”‚6lLw$3Lc±p YÙVŸSõœzõ7]ïù©ûjÚùŽºº5¬g[Mé2 Á‘1ÏJ |ÿF£ËhÐgc}‡¾ð¦Óék«ÞõÞ÷f·Ýy'ƒ~¿:©53ôh„ÝìÎÁ›E!D1‘*¨ž§-guº]FÃa½,¸¦idYVšªB(YæB)¨|‡J0Ö)„Q ª"PCŽÕ&$¢fÀÒã’V{ TC•£ÐtƒvQî;²¬Z(.Ã¢Š¸i¥–ÔZhõ¹á­ ¥ÖÏ_l«ŠVäºã×R ¯ÆÂ¿Òü‘*á['ЋHMAØ©E@³9C|µv@í/©nÅý#Š5,3#ð=:½U¾ú•Gw^‘L¿* ‰³ã¹PäAXNÙØQ¹‹BˆâAåS|vcw†ã‚œ!ÕÒW…_UÝ^Oî w¢Ê9¤F=ñ¸:‰K(Å– AÃnnN'dºOVð â7¨µªÕ §Z•V×6fá¡|_ÇQšŠ:vù®RGPÇR‡ºA`YèZKˆR‡,›ÖûÇÜ6LƒÄ´pÃtJB¬¢:ñºéjsÏU(OqnfÌ®#Ä­:/‚±Ú7(ôu,¾è,¥Zt!ðu•‚óÖ»>ï@úèEº®m@—ŽT…; Ù”qRÊ>4M#¿–9³ö^à”È?Ë,JÊ ¸Ù°_cÀç7?"XͶ†WXÏ÷iØD7¢„%¡@‘/ ¸ëf­ÐW·Õ÷¤?<ÊAC2³ÉZ7[0ÑZR1å P‚ä:«¦*I«=ŸÍ(¬QàkdÙMkÊ<……pMk‚>Í¡w¿¯i tËdµiü†ð’µU¿Ë&†Ê¡ˆä,ß÷eT¦ú {íÆì˜r]±/î—øü:áWÑÅ |Õ’«ÍiÛ,ÕÞkZÙ_¿8/PŒZ¬#”M¹BP(5Ó4$P;©¿»z\'!ŒtÍA×ÒtTòûÓ,/.H`ceØ|íón{Ë]Œ†}t£ãf×fe_Lì;+ݹ*Â:¾ Î×{¥î*ð7:ê\‰¬’L³3—–•œ†øÍÂ…nÄBR´æÖ«ÞaY«BV÷D^†Ú*#­Pº‹ò0ĹuïW9•*"Pïë±î׃ñuÐýZVýz£š6¬ ·Ø®úíÂÒ«‚­ 7€èÇ ©–³ú¶Õ$Œ¦%_ætz«<{ò%.¾ôÜkë„q„adIгrý DõŠý4˜b™–´@·ÊU© “¿p|o–Q(ÞoZ™t ªÂ\‡ê{3¤0S 뽎D"ª5«¤ôTŽ!¬ág|ß/R›ç¨Œn÷CÜQƒ!v­Û®·6 cÕ(kkÁëú<Œ*y«’nªbQ­eàûsÖ¦†½Q.Å÷<)è‹\=V}¯JÄU…º„fùºõ<Õ¢W-yU‚O:BDWÏÒâ<½ÆÊ«C×Ò, I\,Ó¼Úïþš(€ibâû>a’Ò¼Fèd‘SC7Óâµ#¡ž–ø‘#ˈky^)Ëðê$f£5•®‚zî"‚ðZ£·ºÆ ¿#BU1ˆ×B¡hŠbéÇU¾a»IŸXÂn©d YI9w0ž Ýº4÷›÷íß/÷³,“œ‡z?}ß§ie%W«4éÒX&s-RîïϹA¦3\ߪ/bßU«^Ǻ/²îÕcª%¯ -*ª {^IhJدZøjõ x-¶ª ‹^š% Îê××Òͬä~$»ý×^4x6i®7­ê,š8ÂDYè9*£ |®Éç\¢ ”É«'žÌ%P…¢Ê Ô >À ¿#÷Åkõ}€x<¢wìæ¹ãÕ׃þZÖ!ÓFµ‘=mÌAhá[»FƒN·KË41‹8xÛÔéu» †CÆqJo5Öä÷TÛJÁ송¼ÕPÙÕI<ë%úÒ4ü,+Yô*cŸ“¡e«.öÄzêº UŸ‡îBèQ¬ùY÷jêìµ`ü¢­*̦iHˆ.Ø{ÛjÖ’wq I~£Â}-Ë/Ž%‰ ÅŠTB‰ØÆë”´=žAö,Môy>¶bíUPÇŠ× ¿eZ¥É(Ùè8)ÁǹpR5>¬¤O#­€®eAEu:¡¯ ¹©$¥9vlÞ’‡œÛîs÷‰[8·ÝÖ9{úMw_þ¾6*e? ÂQ ×m£in—Þêšz­=ûÎ=€á^·[|Æb>CÍ‹¨òaM’”ÜW`ûp0"_2i2¾†oFz)ÿ>(jóm«Þ_W…^ð`A®®¸áÌÊfsÒîî­ÒÉGõÑQ ¹êŸ× z's>ú"A^ôþ"… ß2¢ØE7³\!„Ã…‘¾í Ùí—}€þЧáh’e•¹Û7˜Â)Bh2ãK¥–„c³D‘jX±Î©Ys–iiys‰Eu!Ä:W@ݪãðújI oê¶1Ö÷“l_Æ8~+‡{§9ÿDÄ‘v—³§Os¤@ Áœîúœ² ‡%ácõБÒgßv‘ôbÆqʸFøå=¨DÝ¢0Ür‹`|] 7ÇÑÊ™sû.¬ú°ð‹bî*¯Zq/Èd*-éŒæ ΧÓÒºŽ: j—t= ¨]Váú"ýF-ÿ"᯻fù‹„iÃ'Íòã†m—¼¶€•bÚyzh,¡Ö`‘„\u¿H!DPç"˜zLV“'ž£‘2"P•dåd ’w³ÛbòTæÿ‘ã·Êýã7ßÀdðôãWI{§cdã!ƒáÃÇóÿÓÚ]²ñpN!Õ%Õœ=}š¶©3.úñqz±ò¨f=ú….˜x‘=w=÷=O& U}ójBQÒ—S ç…[d9^Oð« 5Uo3ë]}ß&B”9¨ 7DÜW°þz\žßˆõ®S×{=›ÿš\€¶îs… Ðt’l¾f `<ÜÑ´4ȼ £·š/Ù•kï˜À÷i“6×HDu¡-±½^'aÃ4ðü‘`$˜óLwÈt‡p:Ý)Y/Çuó’e¥]™:ÁÓ}{0¢éä þp0˜S W•À¹í>ljìû IDAT÷9rì7ug«øí̈ºV'¦E3ƒcÀ hí.«í.]Cã™·¬ó¾É*iï4Ú9BE–edYÆh8DË:@þÙã8eÐßa<Èh÷4y®žxlObiÝÕ^ŒÀÜ MªòMð6v+ᵠöf™,…–•5ùðÕ1œ& ›cÖ[úy+Ÿ+’˜$žeÖyÞ’N® ]®ä“tfŸ˜sŠ@¢…ŠÐ]ÏjϘùlîÿ®wEd_Îò;sì¿aøÊÿy å~Ûä3 Gc:å_²p]£†¤#¼ ƒøŽèu×e6–{ƒ9þu–Ií,²”‚¤É-uÈ@싘¾J òP(•4\ä\Ž9t‹>8VXSñ½E#La’I7á}XgƒíÀ 9v¬ÄAÌ~»G:pÙ¼|Y*³iquH) G¥Zà_'UùդĊވ–A˜Î­r,,ù"áWzfYŒcšRÓœRß¿2ZXŒÔbÑ-‡ ²nfåóãÂzÆÙbX£¾ Óó}‘ª›Äš‘æJÈOi¸ºü¾žŸÊŒ¾ªÅ¿E3Ò\øµ<0#”ïaæ+8åÈûuRòi¨®v]|?()é^Ða:õ v6¢qÎàZʠŠ@å C-ò fI3þ\ˆK¸ Ó`V$šÏ/¤¡*¤‚ìÓÚ]²—\X‡3£G™× +“;G 1Û£Ü}8óäcò=¡˜fß/{úýZ¢NKt7C¹V¨­*ð×+Tš!ƒ´v¿Ú_lô,~M3KÛ,‹Ki²×ö8N¤Uóæ±òê~’¸4\].-^'ô×#ñ7MѦAz¬%XælýÄœÿ*ÿŸefD±‹ø–†Õ"‰B¢xŠÕ(¯ƒ¢°ÊÑH"ÿõQjšë¬ú,'|òúî ø³0Œ`b¡ðãÒ)Ðe:õ¤¿&:»¼Rd ,¢H,©V(¦Á4Ï1P‚p ªn‚žÆ¥°¢JV£*8·Ýç0p`8æÈñ[‰‚!–“+À3£_iåç´pd}ÎKo}ùÉâ ž¢ çëŠe­½P]QYð6U?~Q2ͬ^ýÚðRíR#„WM….™˜ôªÐ‹×u‰3ª7\½d%M³,du.^ %1O>g³´ÛkqB@eüýˆ;ñ¹ù>¦9C¦iÈ~ ¢³¼ú=rгŒ «Ñ£×¶ò^‰i ’f)Fâõn°IÉ·¬‚鮜<¢=¤dYH­‹&Â/k4º9aS2*yÇI~n D7—U†iÌñ iPŸ_,èQPÍ1ð½XipjÑdZ‹„8R‚Éöe.®ï‡` _~yÌÙÓ§eÈ0پ̡T·/—H@ÁOT¿§ZòZG¬V­}•Ïk¹ÃѤÊÖuÓUyŸªð‹÷Õ÷¤GÁ\ŠlUøUKªÆÝ®! 0zq'„j‘å¾®…×;³må¼4 È}&˜…«á6V°ô]r-FÃq)}¶š­æYÈÆŠ*…۔╉¿–Ð×ÅØE7 •|«#ìÔû©ÆÒ넽jí%ÓäE/óîAç°Z„í®ç£_“©/H£Ñ-).ÏÉÈ£[Aã¸Â‚w0LC3°œéØíâÞŽqì6A8&JAÓ MÆùb ÅC £|qÔ†«ø¾T ©æ¢J­#eĶR|Ú¥ÅÂ$å•6ù–9€ Ȥ/\™Õ´Ø\zò)ZUÚ{¹ùÈF‰°Q´(@,§¤g¹•B$öJ0öFŠ®ç¦1—SPµ¸N Ï0s,ˆ<²,£Ûë•„÷ðW^‚ œ?~1PÂy‡ÇC.´» ßv‘îç’u´$¸ná¨v]´+¥0UŸ]ì«,ýø¢äµ®Š-·Ø†âÄs‚¾¨Þ½þW¯/,¿ðÝo$›®®f¿6c/)ÓÑö‹öwM66:hZ“” vÀnÜamUg§Ÿ²¶ªŒE”Å!¯æ—JÆLÆ>Y<š¹J#—ZM (}]sH¢‰4\ GCÓl¢²$”‚ŸfA ©¼Úµ^Õ¥QTt–IÁ+[Ç6i4W ·ÏdÔçÒË'i4¸åNöo4@ïÐPÖC(@LÞöJSv¼1L«Ôß_à&T­¤H0uQœÉ›«úÐÕä&5)FM”©+Îベ´#A!ð‡×W¥ð«™…"„Ø>¯s6έÿp0`DµuÕĹ¼záŠÕõ›[K¯ÆÔã8Á0­’"ž•é–»áºòØ"¡RË`U„Ü`-¿ªT~{šÒ%pY°ëU‹ï¸®Tâ7¾G†¸mô½¶ÆÝôÖC Ëñ#Á<ÈÞNÀ-Ç>ÆK§?Ï£ßxš4=ÀÊîi‚1dÞ6w=¹pG)ÚaåKÉ7Š>bqØžá'e±ÍF¾ R¬AæíÑ‹ù™}ÿ{m±*•NP|wËÑIWF^mµã«RãÝ)n£ƒ£”Ÿ–af>öo4xYå «ué¥oré%hwÚ:t˜ý‡nb0ޤ/9MrËÓœÿa"Š0KÒHE°(zÅÑ7 ‚èîS-4ªþª‹ : Oƒˆi±ÖmÏñjQ5ÑG(× ‡y¡Õt’ÖT ~]XN¹W•@])ë"ˆ/~«Ú}FUâ=¡ªÐ_¬8W½VÝõÕ÷êØû:B¯ çs4`H%“seË?û­¹_¾Ò\Åpqx}ʘuÖ›=º9§Óîi4ÏŒx>2h÷4î>ÑäС۸0|É}œ5Ãdœäkt Šõ Å⦮ë¢)J.s]\ÀŒ+ï`:MÀTÖ{Ô I±%«_m®¥&S?€,Àq2\˜f(y7¯|¼ê%…>ñÃ?œ¥Q$C‚]Vz0Žøú^ÆÛ—ø½Ï~Žgž|ªö:{÷íåþô?äúq”“‹bmtñðšÍÆÜò]Y’αӎmâX6AâXö5›e¨.ƒˆ¸rçÅýéõ-PØjª±–ud²Ž¼F–Í¥øÛ§Âé/Ì®+ü¯4kN]nZXЪ%‘Ï^PQ ¯Z÷*„JãZ~¾Êò«¬}õ³ß—?ð}â8Æ4MZI@ÜZ/üå&!ûXk§X+E^Fx3­µS¥kmO㽕ˆÑxDaé1W··ùùŸý¾ï¯ü Ïm^å v™Ÿü~ ß÷Ñ‹eÜÔåܲb„IBÓ2ÑŸÈr0u Óºö<¼´µÅîdÂJ«UâÅÒ$£¹âÊš‰, |¿PÌ1ºÙÄ0|¢ Åq|ùO¿ðúq*üŒ}ÓmlsÞã¼áh<ôÇŒfè|à#åßýÝœ¹p‘Ç{ŒçOž,C[bö¬®ðøcßäù“'¹íöÛYß»—f«M³ÙÈÑ£å~N’–úâU}ŸÑîtÇŽÂkòj’ØWIC-H.Ú «Š@my."³Ÿ<§Î—÷ŠÕôÄÃ/ —Tá_Ÿ_dÁë`¾Ì #*½ß0»¥Zöº„Õg¯ZjÕ—W‘’ Û«ð_ |ÿšE0*”b(NIã©l}e˜Ž©Óp»C³¡¡›x¡ƒ¡;¬­ê¤i]EKÛžf„g'Œ;ùâ¢"1iÜÏ›­êI@CwØX_çØ‰ŒÛt4,çL]#ÍÚi†FfVyþ™º™ ¼e/\/Á°À1Ê^/"K G“+ žM¬P5œfd"iÊmf~^ ÄT&5½®$ èc'„_]+`óÒ%žxüqîzë=Ü{ßÝ|ꓟ$|ž=ù<}á lmnÑŒ/³Úëa½÷Ÿ?yNž¤ÕYåà½lغ †Û4]‰ÔÌ3:DÄÀ÷YR**R©KA¾V:r"¨®‡PGNeÕJåèc f!V…_XÿªðW`!عõ˜/…U­y˜˜ä‘Š]ÄÐ×…å%׈Yq<›u•sUe&¦dïßÃ,2EæÜšg2]s0´–ãJ–Ý RÝa½ áœv`ûz ýKCÙ^E¬_hiù=ðt {Ã`µðÉݤXìÕmáD¦ë²«›28ÿ›¿û‚±ÇG?þQ†Ãkû’øÓB›hÉb߻麣I§£(ˆr¸..ni¸:šf–Ü6×Î ju½AŠ”à4~õ2üª@uÕÓº•sš.{÷íeks‹gž|Šgž|ŠF£Á»x€ï¸í6îûûŸÍÍMFqSëVÈdÔçùQŸç eðWÿÆ0¾z… W'¬v]Ò(*}f¯m•ƱMF£ Žë–‹·›l*(*UE V)ª‘ƒº—eɺyÍ\=uK%¸~”kÓùs¿ê«“¢a–kÚEcʹ¼ ™”E-C=Ö¾Ži-«E(-¿'ež@ÄëZŠJ$ÅôV‚•AÚa7îÐ:¦ÓÛŽÈ[äëôý?L°ú/å´¬•/ZÚ° v3‡U;È ·4fœd¬¥ #C‡$_·Á$cW7± 3Égê±ÿèQN¿ø"|õaÃÅi78|èSßg5ƒQÑRXó ñ¥UW…<îtæ„ò¶®Ãd4–ÏÏ6tt#wq=/"ÒÌÒú” GcZÜÎ,Ñ1 Ÿ,Ésp¢ }}€d¸®AÍ|g±d6@4Ð[=À?¸ÎçþãçdhËó<úüçyèóŸçCû÷ßw/ûŽß‰Ût‚`B4qêÜ¥¹Ï›ŒúÜ~çüοý*þйxy“K[[’/kÄ9¶)‰BM7èöºr ïªòo_Át4VZµ ’ñÖrËëRºqQX@¨Ë— DPeöUe zÔ¹ uKL !¾^{«jÛiÑ{^åªÄª©Bجf‹,›ò¡O>ùTÉï«üX»C¢$áÏüSÞrû[huÚüÈý· †Cž}þy¾úðÃxž'ÏO<5ŽŸ¸•—Ïœå¡Ïž|ü{IâˆÝI$C…b"g©)ºP…Hä.$ºEÆ@¹U´\O®èý/¸µý·PYM℺J+'Eº²:‘Jü-ê|#,x]/»êÊ2²•QIàžvY-§wЙÁ~M3åwU³ïrE¿™Ot·!—[kcïþ.~Ô!M{ìiÁ• r;îä‚?Œa2¾‚帓3è Kr<+Úl‘T!Ôꪢ4Pq+ÅJËeÑNc2Ë$.º'g®‹+–ê­’r–MÓu™°}<±wãŽi²Ö^!&t »oéòôKCVM“q!è" ×pZ… [†Átg!0 C©VM“Öú:½ÃLü ÏŸ9i­Äº‘ÇýóE[fI>VZ‰é¥çnúëÊÔ¥›Š¨€mäVs8sìØ‘’hàÌöï;„ç\Ø<σ}‘óçÏñöw¾‹·ßs7ï}ç;9uæ 'Ÿ{Ž'œØ÷9|èÛW®°¦0è}õ1’Èç–Ûn•™‡B@ÞÛ?ð}»%]çÂÈ×®£Y¾7+RR»©Ö[]¥*Øu™qÕ¨ÞK­N©6_uc„uŶڙ¶ÚvZMp)•¿q) bÎ"'#LоôÑ”•0bRQ æ`Ïžýt{Ž{;+ÖºMß¼ŽßÇÁ}nrîã‘“Ÿå™¯c{:`Ô÷˜ô¯b‡CâÔdÛ3ˆS•~†¾a„¹å&IÉ WºTzÂøX–„ëqšaC‰ŒëX6qb¸MÌ$¦†Œ+!£ Pªdܪi2r\îÿýüÉçÿ„N»Ãhšûü?ñwþGþñ/ü"S¨%ø’ñ˜A!ä£ñˆ#‡sSfbí?ÊÊÁ}Ü}ËÍòÜ;o»•s/ñÈ“OãþùŸ£)!yVa’%Diþ{òU›òtûTËÈ¢  Ë A1N>÷œöº*•u\qvƒ"в ÉûÕ‹[{¸èbÓPšqä¹3}œvƒÀÏ!×;ßö6žxüqþÓç>Çú\NÞ}×Ýüàüï|ÛÛ¸åæ½\ÞÜd:—B{º.§Îõe¨qï¾½ôz«lÜtœ†£1œôY]Ý+]ÓmàX6£Ý©0©J=w)Ô\ƒjÆ¡\KÐueBQ8’éNm2‘Ø÷Åš„V§P&eÔ •ˆ€P°ôI\îR[ξ«°úªÕ/Aµ6Ãó†”jIŒfN¸®­Ò±ÛìÆn½)/}>p“N{+bsývÖŒ³œºÊ„§£oÝàŽ}w0lóÈI8ùÜä%× “¡n`é1 bðvxÿ¼ø|žgeëÊœk±µ¹ÅÖæÏŸ<ɇ¿ëã|è½ä«_ÿ½Õ^!yw×uH£?LÐ ÛÐeéë`Ñm¥J¹,¡Kl‰E-«õ9?àÔ¦«D¢X4ű£*N­Kì©K”ÚÙwòqÜÂ=Ð;DÙ*{V÷êŽt€ÉÎ,v~eº~†í g/<ʸ(oÞyÂãÀƧøÀû?ŠçÅtzëüèþ7üÉü“©‡fè “TÆÐ… ›†Æ®nÒ²Lâ4£dÍvš‘Yfž0³ Žž¦œÀ™a²R €qb£;®¦ebê:¦Þœ#áL-߯YÈ•þ?ò©Oò–︋þ+ÿ,¿Û„!\Ú<Çÿ—ÿL¯×ã—~áÓ\ ²"DØ% rôvîâ%&“ “É„?ùì¯qeÛÆ6·° ƒ£Gò…;û{÷ÓjµøO}‘“/ŸÂÐ ìfŽq¼iB¯’ÄŽFÄ•LCIæý^w0ÜÙaïþýaLoµ…·;ÉW-Bliâûý‚9ë0yö¸ýÖ;yñÌK¬®öØ¿ÑãÔ9¯$Ðãј­Í-þô¡¯qÛ½÷ôûÜ{÷]ü÷?õS4\‡ÇŸjqêÜ¥Òõ%Ï ÅÜuÏÝüê¯þ*wÝ{¯,«.K˜¤’p¯mCÇnæ$S£aåk ÈÐc®äÂ\£•W VªU… މã"²HØ_—ðSÍì«¶¼º‹_Ùkš‰k8v‡V{ ¥\o¦=Ã:Ý5t}€vñ/&. ; ŸfØ2JMâh—ÎØdÔÎySoFS6z-®nosxÿ> “Ñ`È…"N3@fÙh‰™ÆD®›/;æhDiF³°ì®ãH"îZ³†³ÐZ!àqšJ¶½éº ™÷œÇp #`×aŒF¬Ý|ŒïùèGøÎ}‚ßù~‡“/|“ÕÕÓá”еHCxøË>7í;$óWÚ[ýNÂp8×\ÛÓ¥Ùìqâ¶[ùèÑ£²Ì×ÏÕc_JøÂ—¿Èó§t:‡ŸæÄ.ýá”Õnžô#¢YcoJÓ-çJˆÐ_’¸è–_åšß’8ùÜsÚÝo{M'¤aHìBkÛx»V§Ä\«‚­r¶¹…Ó¾S³i9ï|Û;¸óŽ«<øð7J‘€áË‹8³¹É£¾Ï£>ʇ?ôAþÊÇ>ÎÛï¹›gO>Ï—¿ô&£¾üŸi˜Ðïççoþøós?÷H"ŸÃGŽÐ][Ãjôè6 †Ó»™g_…䯩D‚Lô}ÍÐY‹òÄVdVÙ}5ê°hµUøëúØW­5C/Ž,ˡѺM±´ÌQ3йÊh¢ GS}ï%ÂhÊU-OuÍ™/né1ÇÁoê¬èn‘°bx±ÍJ«Åx²Åh°Í¿ù׿Á‰[ñþ¼Í÷qº]â($v]zšF¿Ð·BÐÛ…)]w´4¨…íZ³97qM]'²,¬(b¥Ñ!ÎB)èr¾eÐÐ`0â¸.•&÷¼ÿŽ=J÷è ’íËü¯¿ü‹¤!Üys›—6×Ù³7¤c8»kÏm/6óâ.ÇqY=ÖaïxçGóPt¯Gç—¯\åêö×ùâƒWxøÏ_dg«A÷Å\Ai9a7ÍV8Ô˜Mס?ôi7š@Ž@…B°ôxÖ ¤°üBøë’Á^7Ð÷<=ÿ+í.a’гΑZûØ òÜçÑÎ&•+k{ &¼wöÜ9Ö÷íçøAK»?óéŸäÄûà`¥û-À™S§xç»ÞŽÃp…'\’†^pÿ}÷òöwÜÕ Wxâ™gøÚ#°uñúÈG¸ïÞ·J¾@’|ø;Ù¼”Wií=@‚mèŒFYŒ$‘äÕ\8yJ²‘ÔÖÌ­x¨@UøEÝ·JöÕõ±7̼µÖÔ Km¦ªÉ:Ö€É$& ®0vvÎÈpZ$zƒnºmÐ`žq—ß5IólL% ¶Ý€Ý‰‰fuùÇ¿ð‹œ|ùÇN`<ÙbÏì RØÇʤ3,ˆS“QÑ:mÅ2Aͬ3f–\0è¥{aõ§…%\£†+Ãkž(ì{¯×%Œ÷òº›OÜ#‰¸óçÏóè—ÿ[W/¡›º WC—g›á0ajôZ{Ölö¯Úì;þ)ƒ‡âÜÙý\¾r•o>u ôÄ×¹ø‚ÍåáIvƒs¬8GHÒàF¶IÓÞÉ€¢‘;M£G^ ±‚8ÏcIbdB›ï¤I†edY\¤H;¤F áÿ·:¾å(€c›Òïw,›@;ä¿Òrغ´E²ÕöÂk|æw~‡ÎÚûÓïD_·xù¹¯òw~âûùg¿ö»åœéþ„n»Íå³—x«|üŸ`óÒ%®noóµGá«?Ìá#Gxÿð©÷¼‹¼ÿúý««=zÝ.~ùQ6ãÔ¹K%—ä›Ï>‹çy’kè­mäË7‰,¾¢k‘CÃÉ…ßH#²D¿fÓ’ºÜ‚E;Tø/ÂP˥疕NL™Þ«Æå«‰:Y3)V™ãp»kÑzËÛ¸ùØQz½÷Üý^>õ“I>/Z­çÏŸçO?Ã¥ )ÓÉ ¬4:ìÙ»ÊúžÛøpÇcßñû&]CCÏÞÍʽ³g÷y:Ýÿ€w<û{œ{&Ãû’†ckùZ¸`ºtãm<`ÅÚ aÅËl¶5×hƒ±Œâ' :éLäVâ)i³°_”J¯ Š0-éHúþi—ƒ^w ˜j×udþºÓ´IÃ,_ Êvh9M®^Ý¡Ói•üy¡L˦×[eks‹–ãpx>ûÿþ&ÿ×ÿöó2XZ¨0!ÞÝäêÄewø=ß‹½ÚáÙ§óÜ‚ÑÎŽü|U9:w Î]âR£AïÀ¬¶p»{¡È×nن̨ZvU¸Yÿ*ì¿VÊoÕò7 ›æ\ž¾Z;_XŽKS0£×r0t ·¨%5Ь8!èBÈã"Ät×ó IDAT–¦Ìy1õÌ0éxS´^+—.±zÓAzÝ.ÉdÄÊê^.næËTÿõû1~í×~†ëH.2âZ¢¯4Å qŒrC²ºÚC7-ºþ.Éþ›ˆ wj}Ïî»÷­RÀ!ï³xúÌ>÷?ÇN“ ûEuLº·ÞÁ}÷gt}Zþß¹³ûyî™./Ÿ‡ÿÅüÞ=ùO4~Kÿ]îèüKŒÌÇ×¾?¿Î©Œ}é eL3ј Y븚ƒ—zhš»M‰G6}-¼P–‡È¬w@ÒG‹· ¢aaÍBðMÿOâL‡$0Ì¢ ÷D/"o)––Äy6 þr€$ÈÒ$_áÖvp²³`Û‘ 2†mÃô ÿðg†x8â—ý×1,<Ú=Àñd 8ÁÉ/ðÔ…'î¼µ$˜+¶ÁÄŸàº.“`J¯¥q[·Åê;Þ!aü\AFÃâÙ ½rŽ çÎýØ>õ×þ¼çÄYÈ£>º0LéyÞË'¹²8©Õj¡wº¤šIouÞÚ_ÏúW·2„dØVZJg®úý"—_Äö«‚®ÖÆ7\¥õ™ÝƦ˜Ž;³æû^£S$ÏŒMÚÞ”¤ã‚e²Å$P›8¸–Æ¡c·òs?÷³<òç_#²šMS®YøÝßõ=ü‹ÿý_ð‡ð{üÖgþ«½Wú[yæïËð`ò{½.7½§Ýà¾{ßÊ·ÍZ9wñ’\qIX÷ÓgÎðÌSO³¹¹Iæ8ìm­pôÈ:oéö¸ùÄ=ò»GOðè¹ÊÅ>ó/ògϽL¢àåT´òJkŸ†yKÌÆ÷B"í;ò{t,!¹Ù`pN;Ð@dá% ïe00VÉð!éCœ“Ï™}˜=/£/*lñ VØ…RȲp.¤,"¡¬Ž¼2ôÿr€d8v®‚iHœfК‘w{ì ‘ërõê4oâþ0ï¹÷¿üë¿.[‡ýÝõü§¿ë>>øžûùíG¹ÿþûùúSOñÈ—¿¼ð÷Šâ$ epìæÃØ¿:Çä_O,êËo[)a¤Þ*åè‹å¯dÏû ô¯æî[fVjœé´5ôÔ¼fmº^¤ÊjzLâvX)„¼‘†˜ºN×±KÝvÆ~åWÿ'>÷¹?äðþÃŒv§¬îÛÇÃ_~˜^·ËÞÿQ¾ócŸà–w?ÀðÌ‹µŸ}êŧ¸ùÄ=´Z-Þòw1Úz†G¾qžÿã7Ë‹8u!Ä6·J®@g¥ÉÞ¼åÎ#|âû?!¯Õß9ÁK§?ÏKÐæóßsõ¬Ë¥ ˆ<ÜdÊnð ŒæíXŽGÛth «Œn†Û TmàCö2‰vËì‘A˜wç×°Âm2³ ûó{çŸÎ…_ss¡9Øý º¾Kš®€Þ£Ç¾cQ±2vP~±Ÿ4SßA3‚R0§¦Åx¸£ý¥(€¼ÀÊq4mÔ K¦;\õÁZoÑâ1¾÷û¿›®–ÿüûßà®·Þ#³ö¶¦>+-…H³óаçNŸáíwœ(‡×"Ÿ‰?áÂæy¼é.ñxÄ]¼hü?ð©ï«US/"è­³Þn3I™~êÜ%N»Ä¹—_âÞ;n#s\>þ±ñ×ßý=m`~i^4û Y¼ƒ6ù:D—s¡ˆ.炞Ñ6Æ[J9Ä›/ïrpo(]€4Ë}~¡ÂHÇ0¨ÈT€eæ.À«íðmQù‡·$pš3ë"–£ê6¢œžÀ׿~†ÿä[ùÙBŒvvXß“'¦ Çcs†þOy޾ÿþyžŒù÷ïXm[LŠ•ÿïóoyç·ÍqBi\Ù¶Ùw¼CË0 Yγ]‡S[WùÚ#ðÅ/~‘Û¾ã.î¹ï^ÞþéO³½y¹6·@U.×"ÿê|~Mkâ{ÓÒÿ è_WõWí´S—üÇ ;ú•j“8eà ¸@jZì [ìvbLÇžcÛÕŒ2ñÚqZÄYHg¥Ét.ÜýÖ·ò‰‡àŸþÊ/sùì%Ü^|«HÝ’Ý´H㈇{­xï©'ž ´lžzâ ¢Ë+Šr^ç`‡·»MBøöÕŒ³ú­¼túó¼ülÌËçÏñèÓ9Bº¼u•0yŽ$8á¶6ÀÚOËZÁ3Ú†zd©ÖüÆdzƒLw‰Ð$Œ·ÉàœFçΈßq HPZÀÙ)v8S†Lž@ó¾Ibu1Ì$dÞ ú;…Ëý£Û·rËïàÐþ}Üqë­Ì/,pìØ±Øàä»é#ÖW­ûú0Úë&„‰³‰¬s3ÿ½«I7—Ì% )¯ÛÀpÕ2 [J’BÄ¿^)GH»,­¬b%“ÜxäHÜ›¿ðâK1À—°2¤æjN/c‹,úÝôëE*^A£Ä¥j? }7a’NH®;4F.¿ñTŸšäøÑæg÷ðÕg/RšwX«¼Ä¢-€h¤Ú'ќǒ–_R¢‘ˆOõ†È#n4t£€ºßé^-¨#ŒZÓmIýcªßOóRt!äy÷p~\w›8$1‘ø^ Ý[ ß Qô z ÉÖ^ÄÀçr l«Ð À Í£á¨eQÐG÷P·Ø ¨2 ÇnüË%€¼O€ë‡PÒùXØ w œ8~œ‘ì§âïéîŠ÷ L…¯çží;8}ò‹õ:{®ÛʼnãÇã .†§~µRį”Iäv0qe­¶Ê‡î;Ì7{|ˆ×N%• n»ï‡è>s:fÃØø2¨á¯I¥RìÛ¿Ÿ·9Âzˆ÷>øn&&®ðÒÑg8qF ¡!éúr~³õèëq€¨Ô3Á¦åÿúÕ×ëÝrßÈ[¯ýÖ›.°R/+ nèÙ¤Íu§ã»a;6n½L&‘fWWp'‡ˆ¹ô‰‹ùýøÕJƒž¾<[L›¥ ‡>m•K ýte‘~ƒI;E³jÒw]?oï ÈOìÆaNž¬òòs ,×=^úŽIiÞa¢:¥Ú‰æ×[‰ô'óteJKê p¤ƒ4z!}ƒê¹¥­î‚š3‹:Ô_ÇtB/ÀÄ "1€§¥Õ‰Ýΰ˜Þˆ¦—1 –¤Ÿ ÝwCŠT7 #…dLµ^‘”¿D£ú "•–UÿÂc@/Lî@è³cýúD`Дʨé‰xüGè~Ýý—M}Ñ€ë’ÉZXz’®`™ìàn2=}øµi~8þ¹½»·qþâ xè!Ά½öëç/¨'ŽúV—–¹é@šm‡®ãÛ1oßí¹lÚJ ,¾ýê«¡v`ãmii‘RUÒ“/°X,rÏ‘üÈ{ÿ¢t™ßþÃ?c ³ÑàÄñãœ8~œþ~>Äîòñÿé—YY˜çÜøåM‘ýõÉ`3ÑO2¥¶+›‰ü{ïMþµOû­·Ì¾Z"H’­Û4êN’ÚœP§µ®Ç ¹a;,­T04l®‹á;éÚÆöÞn …ûö\Ǭ#)_¹Èé3g8uâõºÇ–þn’É$%Çá\Õ@ Vi¤Rlݦ¡§¶¢i=¼÷Èh¬˜+ÏíçÌAù<ò”Ïå)ƒy{…Ec iOG êZ½þ:I_U|à&[vZ5¯H¥¬c‹™ÄÈ„Y± Í9…²‡™¢q>B6Aïk¡çÍdsµÏšŽ$PhjÔÃ@ß©³2xX°S8ÏÒÙØ—î8—±Ý0z‘©½Aj)DЀÆ9„}´>¢p- ,á¸édĨ @ÀºíЕJãI/´>oéÿ£¹€õS¤ÿbB 5 ¨@€®ÄËv·bÇ*Àí[zþÚùKºáç/ÎÄ#¾ãç^a||’îð„X©ÛÜ{Ó]<òçÏÇ´¡ß$æíËÉ ¹B/ƒiÉé:ï¾'·)·ÎxA@±\¦âøø»”vûé'_áåññïéù-.,òÇç=ÎC7²÷†~³*`=F°YRˆ\7[èqµ~3þN#,êþ.ú{fçTÀ—l;øÝ;Ǹþàõì>ÉȶÏ<‘ŸxŸÚ“[,–ØbF›o³!—ªå• §ø¥ìC1µÉ®ÐƒÐ±ÁÃ;˜ž~œWçå÷!hy¶Ç›j»R4+“üÅWžàûýÿ•Û¶]uoÁ÷r{£ÀþnAñþÉD‚h•V4÷ï6뤌üf@±0u<ÏŠÝ{¬dzƒ,¸aôç’|p÷XÜÇ/_¼Èg¿õMVæ)•EX(äÙ60Ì[ƧzûÐ˹Óyægá[ß\ž2¸ìIÖ€ìvÉÚþ~XqçMva°„S¾½Bré^AêI}J˜îн"Â+“ÂÀÓÒhÞª:5›sÈM7 ¹IýT°®Ü ªï÷Ds^•ÙAµ•ô‚ÂRûÂj@xE„ß@&ǼÒgoDp—XA“ãHÑ…&ÇãàÐwlÎϵ’†tgÑ+ÇÀȃ9ÔYmh ÐRŠ Qÿ´9¢–‘"‘ʣƣ”ïaÔ ´Þçèà ½}uò' ‰†î›™¸6 À¶éʤc/À ´„ºŸ›ZàÎÃÛ¹õ¦;5Åž}×óô?üÙ‡?;·0EWoË*É^T£–ýýœ¿8Ó¹ùföï?Ä¥K“qÿzv¢Ê–dˆ–Çyà†ý˜C¬,©’ œŸZãño½È/-ºôŒîz\hH^•x#Î?}"CÓõ¶êJ{°Î¹GËASÝ·{úiÂê°ánß?æT–¾‘&Çþö9ºXÁ*i$vdØ60Ì‘Ã{:sYã0/™`eö^}±‹:™§4ï0ÓÔ©·UV† I Ø+$VþHâòÙÊÃHü8|è¤ÿ² <ÆQ¥½¦°x½þ:¸ó*èƒUt0‡î¬J ACñëžr\Šª‚VÐ)ÀM¦o@˜; =­ŸP?žî j€Õ^=øXwHŒw®{DkˆmÜX'¢Ò’=¤Z‹Ú D=¥’–RHs^%$] вñc¶yš2@’O{±8òŒÚµTyʰÿ¤ƒçЍÿe€Ò¨±Ù†+1…@³¬Ø&Ù–+×£õpiú’É$—§æèùø3§/ðζwo²Õ矚)±ut?“3«ŒíâÀu»ùjèѶP,b«¸zSçÜÄ•Mµ¹®ÆÏóâ /«7µY¦+«Ô‡33x×ÛÇøãÿû ®Üo¤ú‹6¤Ü¢$Ð @ªÀ\|UÀ{ §q›t+ß¡ЄoÕ‰ì¶Í°C°*&÷Üs;ì’yæ¹×¸ô÷]̬ŽóÜ·}JK_ âÙ4ƒÀk¡bnÝ(0*ˆOð¨.Iw/\ÚÕ¶˜>`‰Çƒ$g.týv@sAí¶ëÐ’cm±¤¯G32H,|¯„áL#í „_Œ¾è„Ñé•ýZÛ6&=§Ðt{êg•Ûr¢—Tb€ уÖ\%°†:YW±YÞ%ƒÜÞSèÊîIÏË^¸ bJt´ ‰Wp.ƒQ@˜;‘BGH™@rH”МˆU€ýMè¡#UîLPZsb#h4<Ú•àûI4ßÅ04é°0¿ þE„ëºt”)” (<(JáEÿÚø"ÅúQn9x€¬eñùÏ~–É…UöîÞF¥¦4ýÂWfÎÐéÚÆ…óç)‡Çºyæñ†’·Ýf§¦X¨h¤»ˆ'^8ÇÛî¼èø6+Ëe.œ?ÏÖ]{5r”Âv`mõ Û¶}pƒÒïûmÚQ7}×ßñ]NÿÍœ€Žìp„‹0¨ð<3tÎ^•8S‹g&xâùI–kss&U£x­¥™Yr Kè©ØÔQå¬Tgw$œ‘Û%»|®ÜgpÃ&Ciw¯†}³Üføn(À8îJKQçÎBmü º_Dºó¸~U‰{´$RK‚P}|ŒþûŽ*‘›çÕ쉖DŠ”Bá“#Sm]N^È݉„±4}ì'Ëpw/•±ñ×-w}! -Èpè éÁ@¦¯Gsf‘ÒE&ñCû1…L(< âþÛ&}éãA\ê§,—FCYÑٮ˄Ö`‚OÐß¼'À›N•â…|o ^F‚b¹N2•Šmµ+« ü_ú"þÉŸâçæaþí¯ÿ {ÞÈØèVªN|Dzï3”SâœÉ ±$8Zºh…ÕCÖwðlOºlÙÒ믞äú]ñx( h§îÆžþ¥¹Ia8ÿþéxÀÜ”=¸éÕfþÛ7)mVDf ›¿ë›2†Ñ¹'u×­Û,Õ|íÉIÅcÓÜ™A¶èùlŠrµA£1 ÒÖ¾5@SŒËýX-×®™,,ÒRÂgÚË{Õ<×õ*Æ;œ5 º¾`Æ'hô{$VKJ[; Íõ£Ìëñ»î$AQßîW”Î>lTå`9ˆ4öÆxæ ÒD3{ãG,j§Ñõ¿Ê{ìêˆuÌ€kHÑÇñËÝ@ Àl¯`|¯„¾öM…Y¤÷m£v¥ðR;°Ü|/Üp4h„®@Ñü?øèÜ@:1úÀ¼ äš&€×^=)vî“àÅ›v!Õ! ’šEÆÔ¹xæ5NŸ9ÃSÏ<Ë{íà_ÿêoñäOPu²¹nÜð U+kèYEé=ûÚ‹<¸K¦&™«5(„:Ñ•Çwû·ö³øØ"öât,ŠÅ?åRl|Ñh4:X„ ¯Ÿfgf(Æ~¸uhg¼mh£Hêê­Bø›žþ!Ø>ùƒ‚©<¾—Ž˜l<ÏW‹EB«èøPk³ýîË4)tI‚ÆN¤4T¯íÎÓ0ò«I¬ÄëúVYì•H†ýjì®ë•6ˆg´Û5êGn½OÅÒÙv íŽeÅ)cC‘Þ*š£N| Èô¾0h-š»‚Ö\Eú¤_TÁ­[ Ô3 ´´ ¶ ¡ª¯¦¢Ñ’^€º;‡¬Ÿ„ÀFê݈Ôw6®¢ ʪðIâÓjS–åÆÇ¯W_QËÈ#¤h®"…ÞR6Z@ ^€æ4FP¥°}IðÒ'ð%]©´šóºaÑl:á!Ð:ñ5#MàÕßtЮÕIg™š×Ä©»¤LA­ªVo¡Ì4‘ΩÞ|žW¾}‚ßýã?dzmòùn¸ü؇TPôSkÖÙ–Íräæ›¹ty’‘Þ\,ÛõÖ* tüýµåÅXuxj¦ÄM:õþnÕ¶ýíÓÓ- qqa‘Ç^{‘Þá‘è¹×ýï=‡¶þ¶Ïÿ_-ø^“T*ßAíEmÛ¬w”þÖI{"~3ݶö<œAW@Û!Dá$3‡AK*P®ò-(> Õ£ª¿uöÒ[Å06ÒN}ÂÇxZÆê9I:6ApÞ%ƒâ×®~™yÖ02}=2}½ äÚwµÓÈêDÐ@ê)Ð"Û؈úYôµo"j¯¨ÇØ Ð’¿pМYt{\¢5ŠÈÞ¦ª÷Šú9û"2‚wõ«Ÿ’#>kkÈí2–ÇïQVqÿh)¥-È\§¦#†"†ôHbiô¢ÛÐüu[•ú^ЈۂXÀF7`¡˜Š pÝ·FˆèÀÈyŠªãP­‡]¬kAËR àÛ¯¾Êm‡Ç?»oÿ~nyÛ-¼úâ‹ñÏT‡jµJ¯¥.Þ…©9n;´õzîz-–ô¶ŸêëËÿ« 6³$Àí[†¢e Ñ×ÛËûHà´­7EP ‹Þá&Kµ+±€FÔ_n½w© s"½ —$ºWO•ÞBÎ#ÍAšæ¶´‹Êóh( õŒÄ»_‡ý›\`cçŸ0ÈM½ÁõÓ&j?_ÕßCUnXRÇà_q# çR|Úʈ9ÛÐdÈa¢'[­Œéj¤„¥¼B:Ó—I„\Þ„®t rÀ=Ò&ˆ$$·¶„F¡€)êûU"P-¦4·Kô ÉrHýE-ÔCÁ[+ÙFÀ_û.€ï·úügM-pÊæ5Ñ4A=äÀÓ¹<‹ Ç‘Í2[”‰ÆäÂ*;ú3”ª’%7Ë×þá¿1þËçò«¯rÿbxx˜={÷2_t¹ípžÓ'aji™ë{Zê¶îtÀüj ä{ùü2{´^¤jµŠgÛt·µ ²Y¦T*ÅŠ¶g_›åÞáÃ×Lô½¿Þ¨lúD+·;€@-§èªM¤¿›YD/$\z£kU›¡îC:9ªÊÔÄ`K³^ûfs!0iªÀLûb‹?O#±0‘¸f@b@°ö°†1âaq¥36Â`çsKwëÇÖÓº³èAÍ„ø‚ÀAÚÓHwá\Žyÿ˜{ïU © Œ(B™º¡íH!­°ZjÎ+–$ÈÞÄúP®Kpwªr¿ýyDUMfZ°¾ãö½’š ¥Ëº·«¼¤­kD]¶k¤QÖtʵ4¹¤‹ï9H]ÄÈÒÔiFè…öÕs¼:–‘Âh.¿5Z€µpÚ.h6ã5N‘ ÞôH¦R®«vÏ0­§y IDAT¥R”ª’Þ°¯²gl;çΕé3«è‰$öâ4O½ü²«U~ìcãÖ›ðÎ;Þ£Þb+§Xžú…5²Iì-MçÖ›t”òF2I&Õ*éÊöÇmÀÜâ"·Œæ`& òFün•úN(팾½ö¿]d+ÃloêÞNÿ­7ˆˆZ¾L“Jy¸3øª'Uéê^Q­Ñ£Ù؈Ú+¿oôÆ%sûÉ™™ñ¤\¥ü‘ -€1æ‘|xó×E˜CC3{cPê äò£°vLñë"…4òˆÀAx+wQû&¢qFýsg”Ì×È#­ÝJ9h"Ò‡¹Û‘é}ˆÄ@˨#1¸‘ŒžO÷:—¢ §|;ç¥PíÁ†à ÛšÀUXƒWV@`b@µY"Õ!PBËÆ¬…jó¢%ä Â-Kºa…þê~¸ ­Í líÍwצ„ŸbÙÐgÎs]Ò ƒzPù´N£Ñ`êòx, >~î"×ìä;K‹` Çrß^K§a;¼òêI>÷¹¿¤O[åþæW³Fo_ë»É‰•)lõ» ÎklPv\#ÇÖ½kö%eÖ¡™Z Ýi3¥¦l!éͶ¤ì—¨Ë éè±·ö„U‹oô¢%õ5 JfœèA$æÕ¬þwyŸ¢Q`€g2V¬eˆô Ò[Ý(KÖRÊx$ò€žÌ·ÜáœFsjä¶Ï‘²R¡P7ˆA¿¤©ÇÊž^YÐGžc7”(èMª¯TâõY^ ñ‰¡ êM/ÞÄë7Õz°?ú“?‹—{ž95A¾ÐÍÄÜ<=ñƒ[Ô:W³1ý%Œ®~­Èg?÷efN¥õç„s2} ©Ô6YÐSý(¾WŒË}Çócà¯áH,ÃAÓE8ÐÕòPS¡¾o^“³ûš$€ã/¿,ÞûàƒñIv£Waz¦X¤X*±}Gkjïºám‹ ÇA$òŒlV+¾êu¶ °Z.ñ~û?ðóŸø9n¿ën~ñÓŸÆH&éËF%}{Îл¥Ÿ……^iÛ _›$ו£/Ÿ¤Z¼=¹Á„ôûž—6 ª„0C+0cÓ* Jí+Æ;úÕD+ 8¶—‡ÑÇÐ×ÌÒÐT .;»Ù’YbN/(P-8«ìª@õlj­ÈÌMJnk‘(çŠf¹]ÆØ½Å•PÐrÒ‰Êç ÉÌ!¤–DÝJVktdzüº·B=äõ}3 æV #EàÕpœI’qÕª€Ù´¬J F´­zÿif/WCê5‘QñV;°€´€€/¡âÝHúYIJX8ë^‘F†³Q‰/^R]ïh½Žö¹ˆ¨>]/@b˜¤®“]Ä÷ n;X†Bý¥t1u­ƒhØN‡ht0\ Ð5gâÌnš®¯áŽJš®Ì… [¤Æ›\XËsÓR%b³¶B©\¦XT~óE—±î"ab&-NŸ<Åäø1v_7B¥V⦷=•âg_{‘íQ˼¦K±\Ž•8xe®B&•#‘Îsœš)XÛ 0ZŽw$é]ÿ9Ç‘X–?ƒøgÛ@Û·E÷†¡Çÿ÷ÏDè K=h5…æ^™å¶›nÂv~ã×~ÃpÃtw(K|ýÑGi4è½-0°R+a†ÆÊò"—ŽV¯ºÆüª *l)„®á65,K„"Ýð=¶ýVÁ©€G"e¸Zm“ào/ùÛƒ?Zéy°Ð¤Òm-X˜\ß &u¬Z‚¾ÌE*‹¹¹uc³Qž6Lt/|~±ÓÝÆ+""×›u·ž]k—5m?•@¤”ûÁ|vc xµPyØ69-S £iäÕ¨mUW¤ŒUAd 6B@YÙÄ dæfÅÉû•–jOKªqàuÏ#-`yçú6Ey¸;ç;§!}¯„î. Ã9ŠX¤çÔwùÅÖ~€PÄ$Üœæ2Åô%ê´;Ÿ¤X¶I'-R–ïŒü¬d*tvãò_èÁ5‘_Óàz’Å"€Mw>€ Í¡á±Å1¨%Ÿ¹¶;3CÂj¥ˆ™HóÒ등Ærq†·xuëV&æ6h±¸Ho_wÝúvþî«_¥|åUž|ô[Èd’O}âgùÝÏü./<ÎÄä$ÁJH³5‰Û‰ååU&ª°d'§Ožúž™€öïIh޳ñ%‚^h:fÂË¡Y1 ØžÚ}‘ J  ~oBZ-Òé¨ Â[€ÅÆ·¡™P£©Zi„ü¹l 7-áŠ5Ò­5òÈìMWϬŽkðñx找@Ä´/áˆJhaX`ô(ÇžükO<š3 •cà\¤!ëA[!®…T¥–]nA¦ö©Çî•;¯ˆ?¹ Ý(à„z> óO1äžPËjŽ_îŽýÛƒF r7x5„WTB©X4¤’–H*ãÒh+0ÒjteœŽxˆ& -E3ðc0+ ÑpÐÐb3ð”û“½üÖJR6"ÁÊZ–•5Èeô¤tC1à¡é¸oŽnçOŸ`öž÷2¶ç:çç[9âkóUî¹çžØl¢ª¶Å¶g?ÏVZ€JqBVP¢•Hn¿µ›ßþO_e×Î}ñ$yð}Üûž÷¢õª+•Ë lÝ·%7íZ·”ü{m›d2¯D=m_$ø1‘³‹´¸"ˆ´ë+€õZ€v¾?*ÿ×{h¡0 ~MX *zKa0lcä\4¦co ÐL “¢½"™sˆ¦Ñ»)àš»$kaçÉñxŒöûR…•…VÂ]è(ï¥ÑKºÍ[_MÔå[ « Œµ Õ ¥zGX˜z27ÅÁõør¿JVÁÞÛðu5à å.<§ªÛvƒç5”ó¯WŒÔØÜ]@6ÎvöýaKè#hÁšÿMi`K¡¥ÂÊÑo)u…©©™ÿËHÑlCþ×®Mpí@àyè‰:5,³ ÏõYt À îôvUÑ-ãââújœ dà“J¥¨"àôIe!~ç»ø½æ£*€hO_t;51ÎØÁÔc “Å…EN½þ:/rô›ÏóðÏü,‡ \]c~z†Ç'8tÿ®7ýZD%ûÇ–%:ÄfßwµàïüÝõƒ7Ä',¶¢ãÊÄ~è_/µ”2Õ ˆ½ø`c kñ$à†D³ÝÄ»OÄ-@üY!P" Š“2‡Ïã![±‹çe/]¿P—âªjÂÀ«A(Ú‰’€ÀAÖÇ¡~ÝGD%}tÒ[ðÛ*‘P¨¥ ÜÓ‡» ~w^M†/±ìº²G:k$"5c8Ðà”o+mƒˆIù¾‚'$:)üf]ŠÖv`- ÞJØû/Q÷›ô œE°ˆôMܦFÝ®+`àãKåY ›û”%À“±°ô5eÚÖ¼å„@>¤,v¿O> Š‘€J‘†pËÛná[ß>ÊÂ\Ý×pÛ [ã>Þ±mé\0xåʶo!–üf’ ìzëoyõ&F2sùç§Ö¸÷¾û9zôhü9£¿•óüðûÞÅ×}œÏýÅà*‰ Q¯!´AZ¡«Ànïï×ÿ?ª 6kÚ7ÅvàAÈ·Ä?øZ.Fÿ׿™Hãú§ÊRu‘xýÙX˜]S=rrDU"zJ}¸ Èz=4àpd3} 4ϰ®ZšóopMx÷ ¬Iú³W©#JOBíxLóÉô-¿wC)¯9³J]ÖFÛü4'¡úÑ’àµï|OýÐñÄߨõÞÕj•|Oï„´¸À¬dldDÑ7}[˜=Þroyò‰ÇÙ·w/Ý9U"¼ôͧø¡‡&iYØ¢E=ÊÐÓoÚq{Ш×X*ÛŒ€[ö pjb‚ßüÝÿÄ ×]Çg~ógxÏ¿ú0ÿïŸ/}ù«1ÑØždàÇûZԞѡõo%ãª& ‘ÈzÐL¤ã V•˜ hÇ´vmЗdÙÅêÆáã“~“GJWM&GÍṴ§ÕœYdéÐ-U)´Ùw™áŽ?6AøÛÇigvèllÔf ²ë¤õ5ë b…`”œ¤ Òn©ºßŒE<1øgä㥠º–ÍŒDÒû ½ÍгƽbƒÐ3 ãiEÿµW@ÂRIÓžÀv¦Èº¯Ó½e ¾.E&%±ô,=ÝêZ‚…îUJÅž0 ¬‹“|Wœ*å5œZ™R±ZŽÀw ¼:Ëk:~f½]ø[*¸žDˆF(`‚­^‰Oÿñ©"ï~à~n:|˜¿ú«¿bmyo­ÂÓ/¹øÚ/Æ›zK¥"ýCjJ0¢ÞË‹ ø+óÜ0¤2B=HsaåD ¨¬Q.—bÑÏÍðã/nÙ1«£îèÇ_±I§L^X`ÏÍ;Ÿâñ¯|?ž¾ÀßíGùô'’ÿù3¿ËCú Ÿÿüç9uâ[Lάv$(Ð#ÄöÏE¥{Bˆo};¨ h¶4¦A‡úoS ¬T°›ÒÒ<óØZŸ²±N …ŽNÚ)¿?Õ´hÍUX;†Ó\Vªºì»‘m§r{?lL´\¼Þ1 þ‚$÷<:úø–S.CÞªšš³'€ 0òŠ"‹6ï:ãˆú™§ºë¤(”=d.Ds^MjÊI8åhœ =ûòjãPø\;ï3~6 Ù&dôi63Ax ™>‰êÿ¹/qþä7xþÙ§øì羬.¾Ðå×2 \¿³ÿo/û…0qÜ(èÝ :€«Ñ€ñ^@—H¨ú‘GáÈÍ7sß½÷¨$‘ëÚÐ[÷†?ï».¿õ±_æ¾üG”Êe~{KGât:ÃVÖ*ñ¡âZ“YÝ íŠ×޵k¢[wÿ0S““ ogîÒ8ÛúMÆFFb‹²èö·_û¶³Â·¾]×߯áÛßËÇæa~ïÿ’—^;£?ˆ•ÒW3Šßm§M€öõÞ‘xÔ˃î‰Kû ;_ ÔÂûBŸ ¬ÅÆ8š¯UA3£ZP9«ÿÂ`$ÛÒ \Í :÷åEu£:'eï>˜Š”몳WÕÚ­9€çG¤©ÁÖßj(ÑV¤­—æ6Øá¾¡çTŸïÕsHïC²ÜY6v¢+°;4 .jmyý#ƈÎ% ¹3§ž‘ è}6ùJmÛ$©&À úuÁPȇvçZi“r?ˆÀéñ ãSEœ¦KÓuh6$)Ò™‚úØuþ¹»ôk· .s Z+˜éî8èÔÔ¿¶øÄñãüñ–¥¥E>ù‰Orï»ÞÕvâ5ðB“ÄÕr‰wîcÛî}LLN±}KxÚW%EÛèø…ÞæcáÏÄÅ‹ôöõūǶwÚOÎͱc«zöléáòÔÛö”Ø>º«#!EÿÿÌ/ýÏ?÷Ö)ðñUèZàír_Çõ6´í€F££þQàGâ3‘Žƒ¿}9HôA›$8Ä»•l IDATC¶§™NÒŸj9%FH掄²Ù†R´¹ó­a¯¬úèúëˆÚiµºË¾óôqõô >Æ;=î+›k.ƒ~YnJJwY:ª6{Y¸W éÝ9O!FŽ@2s™Ú Zׯ†``¸1È/*»/¯¨€‚†ÚØ»ü—ˆÒß êgHÖÏtŠwh ø_8$*øŒa‹Y©üÁ_ìæ;šŒn÷Md‡^‰Y•(ø³ÝÔ}xòÇ.t”üÇOóâËýæ9Ž¿vžjm¦ë k:éŒJ´*ðãã*>«Ï#à…Tú[®¨×Jˆl¦!ð\¡7zéWc&`ý)Ðh4øë¯ü5{öîåÁ÷¿›o<È?|íQ.œ?­«… sK+LÍ/pãþ}LÏÏ3>>É'>~‡Ô;ƒ@86Ý}­]ƒF2ÉèÈÎŽ  ];pîäq>ôã?Á>ô!*áïzêI£+·ñÏæºÙý¶1~û§ŽËSsü›ßø>û¹/óþ‡>ÒÑà·æüÛûývJp}Ð^ú·(ÀÎ6 }üw³“¿ýcå&¤“«çè¯\æ²x˵‹‰ÛÔ\¼HÒpWZ›u“-7åa¯V‡Å;ÃÈÞtTѲï’c‹4Úo™iAi(p”Pá±þ_ Ðè ˆ m¿„0‘éî8IEB K‹,ÄA†Ò`_HåaàL¨IÁèq'nQUNr„Œ5öàê è´ù­÷Z|Ä“TÊ¡¯ç£¬4—©Õ<†5é!9«SMRÍY–š ú²ƒ,Õ ºš"Ûs¡ãä€?M»Âôx……¢dµ¸HÓuB#‡„i¡[Ùðpðñlª¯‰ööΓ€‡fhÖÊ«â-™f/¼$nÅ@…B/¥ÒJ‡((mjø±_ûíÂùóüm½ÂÏ|êøÐC?©óç)•Wh4Ô+ê0„É®-pn|š‡>ð+$ûM.|g¼ã÷¬ù’-óþÎê9vŒìafQ÷|ÑÅYöc‘Ïå©9zùј?üÃ?TUé2[®ßÐ’ì¹nã㓱dàüÅŠ_ü"ܲ®¿÷b 3Ĭ@‹úsq\0u ×6U¶ÿÿ»Y¯OÑï›%ï‡!“"|ìH‘lÍÿ7ç^©ÃgŸÄ0äîFfl@Ï›SîŸØÏïêåᘶi2ÆI™#ý¬b Ìu}·*ÁkÈÒQ•pÌA‚äHì$µÑ«h<{Byùv¨§nµZR o¼z»oæ¦W‰eàiOP·'zŽmÚu•¼ý6É{ߟdÏõP)¥½›¥˜z¥—zYÆI{Ûö34«)zÛ×l÷Rî(6ß³Ry§Qeaq‚F£Žˆø×5ÝlUÀ~ Є‡:ê>~]Ær?ÎÛ?Kƒáz’Ri…D*ô«¤ [pëJ ¤ÙX ´þ6¿¬Ê¦'Ÿ?Ê‘xÏu·±<·LqqÍ/ãI§ëzÎL,ñï?ùvž?v±c °’¿:¤’VÌûŸñØÕÓÖƒ8_?Ñ;T–—âê`au™»¶äÃÑï¿ûmcÌ,º„ASƒºýT9]·“ï_ß.làÎáŸõÁ ­OW³7 5 0Ô=Í8 jß\×ZÓuÖ(X»Ô$^8ï¸ÊͺۇQƒ»Ä ’>49ŽOƒcª*|XÐuÙD\m3Pá% ·I¡‡{ÍÕ°¬/)Q;¨S6Ó²O «9=|-ÂR_¢+ÍÐn|cƒûؾM2<¬sãaÁžë¡Ñ×Ú9½LLH–æVuÝäW&R[ 渞 xI•þ@üL¥8ÇRq‰ZµD£QGPÁ 2*à5 èá)ï£ uªëšìøZgÐóÉþ-žbÅŸ©#òۭ·‚½}@h£üµÁoz€ÿöÿ|‘ÿúŸrß½‡ÿè²²´Äk㠼ʎ”üÂ'~\&»wl›je÷~äýLýéç©,/±oÏu míçB˜tfVeÌ4üÑŸü•Z³[ñ毿2Å­÷mœ¿íðaöFÙËRÙfW˜·~ʯ=àÛËÿˆ2Œ’@´ÄÎÁA¦¯\áÔ‰è¦É׎ÛÐFd}‡éåe~òû9|âU.¼~Ü9ú‡¶qáüyzŒ€Zqº#qT+E._¹ÂèNe óòø8ÿ:›Ý€™®\¹´ùIžðíA ÿD4 ô4\ß홺[ª­|! À¿jð›‰4žð;Ú€¦'HÏóã `ÙÙÍîÁ%^;s~êfÈÜ„Lލ²º>®À³ˆˆAÁ¤9@ÂèV'é&¦ ý3.k—“°N"ˆ]Š7≻õIDùéM´ä¾F7h¦róm®*å_JZºŽ¹³#à–>i†@§IW¸ˆÓ¸ƒÑ­šT§~&#HÈJòyAo1C­Ø`¼œäÊe·±B­–gZ [íIÊÞYÆg–h®M³R\¤Ù\#‘Hx™d7ðâ^> d!T e8-àÑçi2ôŽÒùþªþ¿…¨¡»·tp½Ö›¬X©‚HY j•?ñž[мAþÍoüGn¾Yõò£Æ‡»[huµRä‘Gá}ü ? ý;v0¿º‰GÓ¡²ÓÓŠiÜRàòTëïÞ²ƒgÿþÉ U‡ß´ã`ŽæÔÇíSm@§ü7 e‰°¥­ð:JÿhP¤øk×W†¡S²U0Ô=MeèûEÕ{e5)yìG%?(+®ô>Õ‡«·7Ó1GWC ØD§ÇZ}à„ueC¥ŠˆâóÁñ}D0¡¹~‰T÷…>“-åæÜÕ?ÔV]ìdtkdTrCüÝÝÐH'\šu ’tB°R\ÁmÀâJž+³¯³X}Œ…ÕEf§§hÔ–i6×Hg $L„©¨º„iáøN¤Fü4â¾>êÿ¥ôU… }õuááøO}¯Ù ¶Woý Àoâž›#›3¿iQ[«*“K£‹ÌÐõÁø„½÷‡˜-[^ä=Ž-iöo\Ñ}eüÿuüûöîå§~ò'ùÆÓÏpôé§6—Ó…¬ˆ÷6+“ì=pø »ví ¸8»1qÄËC¹øÒ%ºû‡¸<5û@.à•ééï"†r7{{É%„öª ¸ó½fxê«ào_„6àé«‚‚MOJj$jedã `¶8ÌbÕWZõöàJlE“ ÔÎFŠÀ]i- I]Õào£Uý¶²‹@@oB'ý¬ knBz«*Á4Î*²Ôõ!PÙ­’Ní„}%ž©OnÕîn ]’-™ítõù¬-étõùŠ):3d[ÂpVk{Ù¾MÒ×£k¾ ©5MòÝ.óuõا«?ºÀRýQ¾}~‰¥é×±¥P»Ò}‹¦3²-@½8À5žÜÒ‹w}ðwêg²4ÝFˆƒ_ýŽ4øÿ¸{óðHîêÞûS]U½·ZËh—FÒ,šÅžñŒñ>c¼;†ˆ lHB.I @Bx³> ¹oB’{mà¾ÄI.Äf3!„ÅÆ/x߯žÍ3#ÍŒ–ÑÒÚ[ê­º»¶÷_Uuu«¥qò0<×·žGO·Z½¨»ëœó=ßó=çH’ föÿp`>’«¦\ÀCª\dvä m›+sÜÞÝsW6oàÑ`˾ýÌŒVEY¡1Pùö·¾Í™3#ÜtýuìÞ»‡‡¿ýuF'Rž3Ñ y´––s6jH" 3› ÜÐG[{##g=²±J@´´D<÷*Ã+³ ¶·á z;Ù´i#™ù³u¹‹ÚÜ¿ºç¿ùƒŽ6@¤5(Ä1~Û®œªò~H`u:à‡ÿ.ô7Bq"%ÁÁdçe´Ø<iÉmıí$ÈMXá¯qÆÎÆo;3•UÓ€½ï¶f$XíN@w˜F½N@O¨4a·¼K<ÞÝ{pù4%}ÌeÚ‹!² GÔ”ïkÓ@«3òL¤M‰æÁJù±qX£M[Ë ÁH ñÆEbj³pŽÑ¿úÌ ÌŽ3|úe^Õ0õ9O°5b*¦U¬©ÍWØyÛ¦ŠÅw£¼ÙT€?pƒß Øv®*êºA@qg‚e€ŒVþÏv¶mb8 _2<ÃÏ— "!•¦à ó‹¯dÿÞ]xî%¶_¹…±o‰¨|úÙgèè[=ñÆ\,B»ê ˆN?Î;nº}ä£<ÿÒK<üƒ¬zŒ*ËÄI–WV¸iû6.Ù]añk\.GÃ# %5Ém7íå?zXœLÍ­ŒŒœåìÔRÝ÷]* ã>㶪 ¾mVï(;ŠA÷~nÞoÛFUùÏEõØÿz»ýÓcI2KKtôbS¬•‘|ˆ”3]ÖV;"ƒb7 —SB1çÊl­v kõg"°bÏ£K;¡ÎrÐÕx6쨃DKɵ&åç8ƒ¢s.%1=g–h5=¸?Ÿ‡îV …7V§'Ëà ln'i!†3<=Ìhê8§OŸæÈ‘§È¬¤ÉeÒÄšPƒ!bq¾–̦.„UBŽ‹gÀ®‘›–YeôþKÄwởøs€EEZÕUqŒ¾BÚ¶ùÓ6×óS”$•R9KccKŇÄKò‹…›6mä篺Ͻ„ÜÛËEW^J$aìÔWoÛ*­@QÊñ5™h𯷾÷}æo¿}–¯ý!æffØÙ/ȼL¾@s2ÉÄüÃ}}<ðÕÿÅþã¹*Òу΅ ¹åen¸á†‡†XY'Ù±«R>lJ¬é<üì¾ß¨ýˆ@·l³\EúK€þÜ­q`µ•÷1.éçGbSØ´°7qrñB‚<á1í^¾ÐþK¶ ªh ÁHVöµe0P6΀ú›£RõT]‰’7\’‚$ã™Iº6ËDò§I´u‘uÒ´D«I‚Ša»\ÇPx#¥¢@f¢ ÝÛd–g7ÓÖ"ô#ϾtÄËçÏ ½†®gÁÌ¢†»Ec‰gZšUª‚ìØB0íjòëçõNÔ7-‰†ïm;W'§7°ô´Øî²†yºi€e „ª*ÀóâJe %(à} ½¤W9€RvsÖà-¿ð6îI߃91A67ަi,êâ‹FTr™êR[SÛê×âo††x×wðïþ ‡Ò#¼vèWd5ú¶oçèÁ(¥ÓŒ~’ƒáåg¯›^äË&‹óó\{ýu<õÌÓŒŸ]ä={+­¯›úûÉææê:qŒUuý "(¯ª¼á/i  ÿU%´&˜‘´-á!²Dd7VDn0‰­4‰Õ`ŠXÖЗÐÔº#íµ´ª5ZõãŒBfónoÒ¦*páZ å)[(±Dƒª#Er$ZÉÎØÜÙ¹i±.ˆ ãw#|vnºbôNžïþÔ¼Iw«L©x–”ÑÏ%Ñ ƒ…× JÅG8ðò³ŒŽ¢åg<ö<Š£ÆÄF"S/¡— \w+Œ\äÚ•ÈmxÑß/cw ß5z—Ý7-lVÁ{ò»ÎÀŸøI?·$èªÿüå?9`ÿTU€çÅøÕ€åBIŽ{Ư•tB‘¡D#SG3rbÆFÁîØÎÅoy -jñkwz“€]c3õ"¡èÚÃ0¿ûÐCê}–[þÝüÜ;nfjv½dÓk¤lùÖ óØ÷…§‡Õ}|AÓÉ ÄC!º:Ú™œ›¦/ÖE[{s³s\rŽiÁBƒ­[”䘫§ÿøÑ‚›Fø÷Ö­Ž"¡5É@E3K¾V/Y4&‚²åw­ñû9µ†€øs{÷6!í]p-p77˜%´’޵2íÕÏ]Ùl$Ò%–„:ý÷¹A4ÉíbZ;®;ñ ¾Êá!qö' QÛ¦á–#èÒN \K¿J<±›”±wÊL¸•Ó€­”•¢Ã-ŒÓ+x¿P ,/Òûa?èÌç„ svòEfgSXFÓÖQÕr@v =Xel³\1ÔZãõ³òõH¼J9O0ý ª3–]AþÇ»Æ/d¾a@C‘V-OQlìZÛ¥,y#ê81EÏ}¾J @·õŸºΗ0xRà|É7ÌÀqË+Ë(Ý7^q•·ÜãÙç†9zàǨ¡$Ý[ú)JY~éûø­÷üš¦‘ˆEßðëN¤¸ÿþûyøÀwîà·>ö â Më{Ã@Ñ/fKç&½E!ÝmAþãÙ™uIYx­Àþüß…ûþ´ T6<×Jö*tãw‘pE‘±ì’§ówU€®ƒpÛ€-oW`HX¼vÞ¼!Âmö4óZyQ0ÿ––&”wîeyÆéÁnÏàÝ€mE~ó—J¼ð{‹Ü¼²ôÊ@öPÛ9vD¦Ç¯ß.8(K¡¬õŒßþ­1ìÜ4ÂÖª¨ŸÊ2—y„çN>ıá‡8ôú7HM Ô`ˆp(î‘qn}@2¼œ]È\÷—èüÎÀr”z~2Ͻô+úÄc+5zWù'IqBN‚XAÕG0-‰¢ulø<Åô/:eD§â;NB„®ñ:Á€^ÃPU xS €’^&¤1Ê&j¤B†"qJZŽ’–CÉw2uô8¦><ËWDØw•¨áOc×7ó®Hœ†¶ ¹lëO­÷Fwö¹ÇóO?Í™á!nºõFþà?ÃÓO=U·d3Å !ƒ´7ÅøÎ+líLx€ñ3Ç×|­§µÍ¢T¦.Óï^úó~×è] °›û¯•ÿ»²_7â»)ªØ(JÔ ù/U &kXÀB¾„dÙ`Íà&éÞ´œ`‡¨Â€×‡¯*IÌ@”.µ‰æMO§IoÜrsˆýךD"ÈÌÍ Ü,ôôã ã£âŒ´Lz6Da¡@Y9C›²™9㌗ˆRž*êù9aüs…g‰É§Ÿ|QÌ–p[aJ؃Ó~vµöþî;$÷Ã~7ú»L½ë@\cw™ü*FßÉÙEd–+•ÉFb»”¥¢uó6â±½rd9ò0Á`i•J„ tòF˜R¦l¹AÀ¬1Q㼨ÏoP/ ‚RÁÑ‚£ 5`~n+O¿p”í¼ŒÔ¿½E¼»îðOL}øe–žfúЫd–¹å¯¿€Ûßùnâÿg޹Ù9þåŸà‹Žòó·½ÛyâÉŸðêÁƒU¬~9—%çtîdâÔ0ï¿ü—¸ý¶«×ÕÔFîh4R—éʬ÷¸Ú–`ÿT 7ªûá¾ø‡a˜¨ŠKJ†‰¬†È˪׈k¦@wN2g' îXp»Œ­ #é3HVŽêÎÜ/CW¸Þñž¶Ê\0h³­»è¥f m¢i…q½`P*ˆ×ã3<)¡‹²aé4 lÁ6ÆÁž ;/ctœetr”²–C//;Ð>Qeð®¸ÆÕуÓU'UŒÝÕÙûëð«:Rµr¯VÇâÞfÙbú²¿ƒ"ØvŽ€$“a%‰EÞJ¶x=ñØ^‚R#v8Œ XRÛcjãÝ’ˆ)2e L«ˆ°)[ —¥¡`Q-ö«÷ûSVž7`êšwÊ…4D›¼üQ+é’Ç›8KÑžcCo©B c*ÀTq”Á–‹Hv¶²tЀ²øÛ]¿r'{÷\Dc\ªr7Ý|£÷»Ëدw|ýkÿÄàçÃfïž=Üÿý¢˜O/“5,ô’M(fi:Å7øô÷ñà·Zw?@ÁT‚Á¯5~7ê—M«®ñ×}Eë°âÁ|”Mj®z.¹ˆÕá+,ÓÞj2w–ÊPMDIâ8^ >'$Ë4vl#Ù 5ÞK›²™Ä†(-ñѤIOBW«Í¶îѾÊÿšYeÙBŒ±±ú{ûã£) ÛPÉÎ=>Â|j ›YŒ¹J),Ž9ù° ³ñŒÝƒäN©Î¶+ÀÝ„ÇcóýåºZ‡àÂ}YªF•çtúöÉD¤/ßF¼a/†ÝA¤AÄmÕÃ1|÷hlí@Óc¨Í0±:ŽÀ!Æ (:±X Ó°=ÂÒߌŸº ðü9ËD Œ)ºÓE`("N¾lË‹“3Dm¨‹ÓÌ.cSw?/LO0zvœ&EE±rœ~ù Á•<ï| ƒ‘0j²RŽܶÛßùn:ÌÜìû×ÊŸüùß2<4Ä…íæØá#Uþ3Ÿú ÆSþàw—†X€­[¶òû¿ûi}ìq^=x|.GÜ7κ!åûý€¿jü¯¾ðàºù¿¢çÅÚ¯bÑkç]‹í_•>8mÀ®ÑG"I1ù×1xw-XÙTêî¨GúG„wji²Ñ!,ÕS}ÌÇ5 ÷18`‘hër‰ŠFšüü‰ Q¯‹®©I!”H6•¼œÞëR›Žjz.„V¶™_2(¬Èôv›¢±Æ°È|žãFVµÐGëî¨ðü^HaëEêf•ÑV_¯Ày7ŠûËi.{/rz'󱨶T=…¤Æ¨í,½ŠhÓ5Äö¢ÐáÕÔ¬à¿m‡‘¤"™ÜjH € ì Ü·cÔeµ‰²–«2KË>¿Ã@Λ° ƒP,A©œZˆG}D Hr…eŸ>ɦî~ V…%í­ˆÙ¹i7•€4ÀØ‹yå¹¹ü½wp÷oüÿøÅ/rÑE»yÏ5äð5óÿ}ûÛ$âmìÙ>ÈðÐû.»ÜsžA/'Ø* .Íñõçÿ‰­;/ä®÷ÞɶíÛ9øÊ+Hj’ mMŒÓ*I žâ·ì«šTïÈêb2p­ñ¯—*Ôr@eöŸ,$ÁAôÊBP߀Ue¿5ƃ™†N¶e;y3BC$E¹c‰•éÚ7ż‰¶²sÓHJ‰æ  5¶‹Ö,æ,¯‹D'÷i ߨf å‚D©…™ùüQFN<Å©Ô딲'HyÊåj0„$É e˨*½y ¼š€R‰êJ•îÞO˜ùËwÕŒ¿`ì½ÇúÙ{ñïƒtþ*¹ü4héë ^–¾Ö¡R=ÏG}6º%‰ëv ¤Jwù½O|kì3âÃëï¹…«·žà[|Ÿ¦¦Õâ•þþ~F ‘šÌ«9bÍ­<úãŒå†nà®;ï䨑£”JEb‘æéÂO½xò Åb ÙÒ1ê¹Ë¤ëh¼ê€³Ä?ý×ÙýN ^+q¥^?K,r yYCê°±i ªu¶5¦#u]I›²™ Ñ –òbÆ}k—D+2MMl„˜*B¤ ;'jåY sêø"cgþƒOŽrjònJýÿ IDATò+“˜¶Ž,©¨Áj 5\‰ðe‹êz»r U]e˜FíQÛtã2÷~x_!íºƒ8$)N0 S2K¨zŠ‚ÝŒl¤T| ñ~‚+(ÓA,^1x?¼Wñ¸Ôú¥a ØÁÓBäcÆ@Ž  èÀ2–¡¢iö¦é‘ý©«Ï›˜>sRŠ%7Ùº¶Bcc‹7D4‰ )#› Òh*›e>_dsð F2ǽÛæògÉ/æ€{XIÍ£/ÎÓÛÕIéìo}÷üû “yåô÷õ°k×fn¼ê2Ñk †AÓcÀÙ*Tò bxhˆ·Þx#W]z©Ã̱¸0牄Îu4r^ßÿ¹ r=òÏMLC¯Êÿ=!P ¡.°ÖkÅÍeÚ‘ä)†i“öAdÂ3üDó £)Ëé/߆TBQ›HP"æ[c®ŠÌ ѵùñóyÆÎ¼À¡Ñqìøi2Ú2Z­¡[4×*:û’óû6bΨtÖ9×EÔ¯äà9mu„¯×çgîý÷Ó­(1¥ìýºCbyó]HlC ½ %®Š¿’cô* ûrûl’HUNÀu î¥$g§¢ÛTW/`¨`ÇF%úË*ùŒ±Ê4ßTUãÁ5­4/e“¼lx%Ál:N²S,`èÚ£3‘ •Ͳyצ² ´qdj™K6Ηäæã˜³]y)¯,=ÇégŸa`cꂜ ì顸܋Îc ÜxË6<÷’×WÐÑeerÊ÷?VÍ“0~Rˆv]tO>öÆ« é¬^Eþ½ã¯'ú‰D’UNÀb*‚‘vF¯EzFê4-Û2q@1»ilÖÈ¥…¸FRúF')ÏÒ¦\ˆè„s²WÏ,$"Áêçá» ÊñsFÿ7À;¹Õˆ·¤±±…|ÉðÒ€D“Ƨžâ¸…ÖÉ^†çæIDÂl(‰;“^¡¿¯5 :‚¶nÛ™‰i†µ" Æ,ý{˜ÌŽžh£oß>×ö' FÂ$/#ÞÐÄÆ~·/ük4ñu<þä“üòõ£ÆÓ9zÚ›×]õÖ}\ráeÄI¾ûÐCë¾O·VoÌ·Ÿà«2,Y¸j³Ó*@Ì5óýZNÀ4tTDZ(ŠBY`¹ô ×7§™Noa 3@wsÅømûmÝ;‰†*leeC¯Î²pú^~<¶„–_ ³”"ÞÐD¬y#M¡ šf »§u¯xµÐÛŸË»QÞ½k »zµy·¶_K⹆¢CB:k¨úe¥ ‰vÒùwa*F¯ÔaîÝhN˜?mß¾OçÎ4ðÔÛ!!CÖ¬_ï{T›Èåm"¡<Ø1Ô@Y‰SÖr#qL#çD¨Ì¨æÕÞtÀ¶u:µ4§ôhÊa›9o>€@&?Çî¾½44%…ÑoíGÏkdµ"á`é@š=ò•DÇI2MÈZBšÔøêw~Â'>p3+™)ÆÏœâ‰ç¾ÇGïùL ‹GNÔ|¡}œ>;Œ¦itè¥mÍÿý¹'Ÿe!µÀ]wÞɦ¾Þ®‚º,¾¦‘hHÔEµÆïFwW÷ïG eS!ˆNP†RÑ${ BëõØfÀû6%¹ºÍx©¢=è® W 4m¦?nyÍÕ ¹g;=– 2?mcÛǽé83g_%³”"’ˆ£ª ä@˜ö61š[3L¬bEU¼¨’Ch†YeÈ•<ßvà½èœ«…î^=_ªÎÛkKwµ‘Þ%ñdIDzÛXB¨E¤oˆ½ƒ=8“Æß5ü5Œ¿æ‡e(ÐÚ¬Òê¤F ›NIªBž³¶— JP.Øyl[_Åúû? (˜e³ÊøÏ— ø¼:€’.’á!zéê(’+ää833aúLFÇe.Ü߯Ê\Š)u–î®vâR z^#“^! søôK$Û‚ÌÆŽaçG™Èç°u66Ú«Æz¥òEŽ?õv^q_øï_`K×es–H$âåðêœFrG7ïºã¾ûÐCl¿è-ë* ‡‡†¸÷¾{yÇÛßÁ]wÞÉÑcGyø‘úó]”q®£1Ùâ9 îï2ÿþÈï¿;°^´¯—rÔ„ÍÑH½£2­ *càôØì hŽ ÁÒiŽMg6ÿ©Ùfæ*ÃOá¸gðÂè‹”lÃcßUq:Ùl/™%/²W—Ý”*¸îBAö­®×û+.IèGîkºN"®L‘3’H´S*^ECÃ[Q¤íØá0’ËÊûòù° E³>¼_Á¦]–ØÑkrdLñnK ‘À¦­ 4ͤ'ªðÁkl¾ü«ø/° ¨’às,½9˜§\° ¨z•#(ºÓ¸´v*ð¦q–!6™Dåò¥¤×ÔÑQD+AG‡Ž4= ½aÛZI¥W8|ú$W_z¹‡6u÷S”çIe³47]HàÁ•<íͲ¥W4‰l¿r ûV.'ÜÖƒ9k0öòWôv°mÇeÈj˜ˆ¥OØ;9åíìjoçCwßÍï»o]áý÷ßÏ•W_ÍÛ®¾šÍ¿ÞïéÖ«ç¯ékDBµ(À/ª7ðs=ÐZú—ð¹jdVvi˜ùÜSäËCLO “ÑĨ4UÐiÄ”UdSG3LJvÉSåγ—%ÁÔ‡äPUí¾v ¦¿dWÅw8þÚúw²—ÓÛvÝZrö6P¶w±¸p!íWx‘Þpà}4w"ý 6+fµÑÖïï}ÜæÚ·)\÷vñ{‰¬ ’Ä·Àü¬ÂŸþ?{¯²øîÅK\âP±ÃØÌ8%?AòK(»äŸŠAš€"HAEmÒèåÚyòyQžW È1H$ѵ” L¾$æÌÌ„¹pS£ä®Î¡/7x¹wïnb† SGçhÝÕˆž¦“›™9FÄj!=4ÇTËq^;3Nê¨h9³Ômt÷†ymy…P~šßúØG¹`ÇFþÛ§ÿŽ­á lÙ8HЪ|SºùŽóäõ ?OÁ;o¿mÛ·óØ𔇦^dvI'¤iŽWúúCA…å•Eà«5~öûßÏh%»*ÿ¯šú³ÆuÓ p†‚”i' S.™š{Š9í,zv’å•YÂËÓd£-”0 ‘FŒ€pÞšQÃí›^Àx|P^´k†éë'{…ÐÃkÈ©Íãká½(÷UOÓ©Hp+Š8U¡`7c)W`¯£°GÈmß@m~›+úMÃþõDƒï~¿]–¸ívÃý¸ÝÇ^Ø,1|¦,¶^àñ‡ ô<>*{ÏeÛat@ˤˆEuÊ&ÈJœ˜‚ñãN@¢ldUÜnZµ{ÅàÑó¡åÅót$ƒ "a–*“o]/$šrHÓ£lÙ`2=¢¡Ê"Z-©1¡¬0<7OKO[.¨®µÏ CïïÝH°¯É+'Þ¸¿x]}µ£ü¼ý‡YÎÙ\uù ‡OˆÛÞ¾çfúúúè½äv²oÌÎÍÎñoþ+ÿö½ïÑ×ÝÅ'ï|?W½u_U«rÙ°™Y1·¬z?I®2~×à#!ip™ÿR±è¥n$wKƒ.p;ýƯ(²7"L/Y,//òêð7yîðW8~ê3qæ',¦E e5o$Ž!lJfÉ~évÒ…ä¡ÖpÁ€R%Àq¿óÚêôÀÛÊ*Ñeãç’CȽ\"&/aÛ&e{ÙÜo‡Æð½ÄÛƒ=è¼wËnµF¯úrøY[â—߯pû»È"‘ÙUS‹l›^ƒùg™ýºBkRÜöÉ^~~û³iååJÇ£{Ÿ„\yMUZ-O7ô4ÁHœpÈv¾÷8²ZoVq^œwPÒ*oZ×VÄbP%ÁJïr®a€Ó 2‹ú4q©-ãÔ™×è5’ŒL1vjŒþ­ý44%ÉjE¤ØÙ”M9cË“ò¸ÈÅ7ô„øÎá')ÎM’šÐPç4zÛv0wö,g_|‘‘Ó§Ø¿o??ÿá{xyäuž:%ÖcMMLÀkGþSïKÓ4^=x{¿ü¼43Ï/Þt9ïÿЇH4$<¦Ö¶uJz™±T™Ô‚ þÖûø¯»€îW௠ø7‹q`v•ñë†D>—c~1ÀâR„ùŒBzyšù¹ Ê–h&kôN4Í(‰˜–ä4ÓTŸ€šQi‹u#¼˜g_ª2þÚ—U¬¾dxNÀæøÕxnôwÅ;’$£—KØ¥ LKÂRö²¸ðë„Cß$æ½Ûiç7xůbò]h¯쪭IZYl(Ô É+å%B6Y$n¿U!Ÿ7µèiËPF”U 6ìØi0¿¤S4á¿!µyÏ ²¾rà˜QDñ=IÙF_vF$—ÊAÏðuç³)–$Lˬ Ìڳϻ˜>s²ÚíËMèÙI¢ò YÊÙ%"åŠ,‘)ÏêV™š(2=-N¬MÝý¤N¿Np%/Ò‚®v::£dµ"Á•I8æ#¿ù[t:û $I%¤‘$•²a3»©8ƒ’½Š´“µŠtÞ’B(Š,‚•AQä*`šatCò~¦æ-æÒ&³+Bb[¶*‘4$‡Dµ L½ä3FÙë¢ó©ß˜Ý ÏéÝÛ\¦¿Õ•U̽ÿ1.‘ç9 2Hˆ²¯¥ì¥`~høbá{éìúuLÇèýFí7x½†Á¯=jëõ‘ D‰ÊüÄ6K%qŸ¶®Ê.ïñ²D‰ë®VèéQ·á¥E›RA¢«­DÈy®i>xµÂ5Û3L­ÈÄc ¯ŒJÈ*NãO޲–½ZŽP°ìƒþÕúùPžW ¢tLÆ¡`VÃ`-8J˜f榢ôÊb¶tWWˆlnœÒ¢„ÕÛ@^ pjD´ávwµ3aÚä•}‰X½ $"âxíä0C£/1°±±Sc4lêäàÉÓè3ã4ÙðÄ£b¼÷¼•Ç(¹éæy}èuæ­<úÌ8gÏþ—ÞcêÌ þéK_âÐÁƒDB¶­T$ʆí”nBjЫŒ,g%ÆRtË•ÐJ¶çJÅ"–»þÛ.a&ZQ”õæ–Uò¹©t€©y‹…¬Ä\Ú$µh‘Zô•þj†W–-±k ѯ¹÷ò÷šÛCrHŒ¶ò ±Œ(²÷»;Ãåüi€°='àöHRÜs‚ÈË I2Eë ÊÅ?#|Xø^" ·bI¢lœ5×Ïë×áÔÙ’Än®»Õ@+ÛjA~î£N*W˜_ÒYÌ­ÝÈD”ÿø&œ® °påå2Ÿúlri–Æ„M¼¡ÙC¦ù|S‡`D@ÿ`¤ž èü7W óC>Hf¦…P8Ÿ=[û6òÂð)æ-Ñ_ÝÙЋ‹ØÝNiÁ$“^¡`Í1rtŽ˜aqõöʽ`ÒД$ÐhPX‚£C¢t5þì3 +=v\}-™H”·Ýt+ÛzŒ!MjlÙ2È/Üñ ¬LNÑàCwßý_srš†¦iüð»ßed¢àE}¿á»¿ûÿæ^F si˜˜·I¥Ì.é¤Òïg!+1»!—ϲ¤ÅÄÔEñêÆþýñ¦eV]Ö3öõˆÛZÁNÙª ×øó}· 羆û÷Ú|Þ°¾H?KØœ"„Bo­2ú††[½H/ÕçÔ}mÝ]]ç¾ Æm¸óHDar²ò¿9þ»ßo0(Áô¬Í£TZâ–‹QJÀ¤“}-/Ãó/šž³);YÈ6E8“vÉ&ÙƒÛaÓ®,ËYgŠ“¡ŒŠëjÀ&B SgU'àùýL@P‘0u(”-VVÒb3°™#K<&±¼²Ä©ñ³,/-066Fwo˜é³yô¼ÆÄK/l Òz‘(÷Íœ|šT6Ëâ¤èÔzË[H¶92þ1Ãb[¯ð ÆT€Ð¦‹é™3ÙÞßÇUý´iažxôaf3oéÈï»ôÂY¯¹M ˆ#.à‹vŸs|X}á“IP‘¼Èïß“Rƒ:p?› "y·Û¶Ž$­/VƒÕå?ÛXònWƒ!oW^uøÿÌÉ#ÆhÙUð¾ ¸÷óó~í}m¤wá½ gÈ`Ù ¥â]Ì/ Þaÿ5ŸÑëUŸgøœQ^?GôW}ù¿{¸Ð~ßÕ Ruz‰(|þ3Ð?ó+M C”àƒ·XÞp•Ç[¦`ÚÜñ>‰§ž0<$qã-šfP6¦¼à缬vÌSÊΛ€êï, (ºñæteÃízkÁ e‹h0 ˆ@¹‰©´Á”,‘_Ë$¢6R…+,óðÑã”ÇÓç&Ù·uÃÎl½¬VäàÁQ2éÞ²ó*ZÔ.RÙ,zEwÛìÒ1ô¶³r€¢eròùÓt÷Šé²Îô¼ÆØømím̦„KOv¶òK—ß¶-böÝ5×ßÀç>üÁÿôûu¿(¿!ûÞïJzÙûq‘‚ëÖ:Ü™w®#”fµ÷ßîž8™ª5mk9-¿±ûKnîß]e_mYÎEþHï¢Û6Ñ­%ñ?[QB¡·R,ÿO¢áˆ6|Ü3zõ§ë×{¼^ó\% P²9ðˆM¿DsÈfÔùN’ÎÇ·u'ìÙ+Q4…³Èetà# °MÁ l–óB!˜¡1Vqªbóû­¼^C¸„Vˆ:i…h–•¸‡\ŒÄQƒõQšyžT€çݘ¾UÆ…²r3)ˆ@ œ™ ”häwÞû1 ½"ºµE«È“pÖ’ Êí${ºI-=$"a::£ŒeFXœœ¡3ÚÝd ´Ïîuè¥qÒv‡ªþ¯þ¾~Þñöw0&Q›Ï0zü ô¶7Ýö›ÜtóD$8²œ¥­½í?÷~mÝ3z×ðCjp•¡»aH zwǨ¹ ÊU†j,y‘¾Öø/ý×%¥¹ ¢Hd¼ô¡àºô§%³äA~ÉOØ£÷HF‰ J`ÃNP*ÞÖÿ†ý×ÄâWVÁûõ [ƒ¾VÉO¯¹Et»dÓ?_¹ßdl̦1&ñ;·Ú,Ø6×]#òýÁí¿ãë úäÝ6ccâ÷ w›ž˜èˆñ†Êgü÷wÞ ±9vD&R(ÙmDƒÊ6^÷ŸéöÿÛ1ŠŽ¡§ë"¶óÕø3qkÕ/ eK¤r‡á¹Ôq¶]0H²³•ã#„¹ôÊ+1¤4es–•É)Ò…§§Oxϱ2W&Ñ)ñì©gÙ뤣3ŠÝæ,›ˆ„=D³]N„‡Ÿ|ˆM ;)–çxîÇOSP: ØÉé™|àŸyâÉÙ¿ïj>}Ý9|ðö]}Í õ¸Æí7h7ç·}N¢lØ"*Òº)@m´w¡¿eÜ—òªÛj¿ ËŽ­:Ñj¿§Ê\¼¨î½n0 "½ª8d^;¥â]”‹F4òцÓÐp«W²óþZ¹ý¹Œ^]ÇA¸BìU×ýŸ={%F'ÆGáïþ>ñQIÌ?pÏÞ ‹Dc#ô´eøÈG%–Ó‚å?vDf¸Wô›üÕûtþè3•Ïó+÷ ÅÞÊ2$“‘`‰cÊñ˜äÁ~÷<F+ß»_/q®Ýo `ÙâÄ÷¼™Ñ_n?Àž=»1s¾þ­osJ›eçà c‹ÆGÎðú‘1Âm=${ºiìéae9MCS’™Tˆ&H˶lçñ'Ÿ¤3Ú͆ö²¹ é€Ð¸mÆzBô·.áLù:£ÝüÆû~™Äœs•f¸ù†غuyàâ×îä—M}ÿK¼‡«p#|H z·»ŽÁŸ"¸·Ùk@=½f°?ç¯Õ¤‘ÿ¶µ˜ÿÚ¿ÕrõJv!9„eQõòÅ<íäÍ_£XþŸ¨á¯’tི†×»]}‘Ý5ú_.¯Ö1þ+úM®è7·á=ûÅu$wK~EºZm/ÚûK„ó³ðè3 »Fm®}›Ð`¼ü¬Í‡î„þ~‰¿ÿ ‰OJ|nŸþL˜O~Â`~nº-Áü¬h£[ TÎb;£rù ñçêÊZ³ó|ù¿éS«jÙÜ›ÔFõIí¤®3dÊ(ñî^žyâqžyêq“qNÎ1¸óB¤D’âÜ$N¼Èᣯ‰/0¡`Í¡EbØ3•vÞTa )-êVi²šèš#•­h¨ã­ˆçâäìENŸ¥` ÄpÙ…oC?›ç…W²½i¹Ÿç…}‚îöî¹çAœ;ÈzÑÞøõ`½‹ê‘…õÉ?yUޝ—ÍU¤ ßèmc©ú÷5ИiIU‘Çòn î9ÝZ"_Ìc+ÝäÍ_#¿5üU¢ '¿Ò‹ê’T¬ŠÐúŒ½ÿHÈ«#»Zù75×>#­ë$î»WáÆëd%›¿ù[q½ŒDXj¾¨,‘N;)B\HzÅlÃÊo0„ ÈSáRwÜàï–xéYøÞÃø˜Äï}Ced î}ZáóŸ7H6Â_Óù³/Á'?gðúðq‡#ˆ{\äí¨ËW¦¬éœß” —I#IjÅ ˜é*gË,Q([„%ñÁ;uŠoþäBM\|ÁEœ:~Œ¿ûúc¼>ô:ϼv”•¹Š!Íç‹ t³ÁC3ãZz:ØvEZ$FV+Ò™H°µgsu¤n††M˜¢¾È{wmãÆýûQºE.øü3_r ×ßúsoh$X­^Ûeö½1é¾èïþî~>ëÁ^_aþe¸Qßí +¶î‰ä½«ìs>$‡0ì–­Yç²-ýañûˆED¤—ØSeôõ¢üZŽÀ5úiÞ‹Åï}\äá ¹>0lûÞisÛ»+rÛÚçk—%ZÛaÏÅpÿŸU×ëUEŒ¦ÿËÿn³0e1j‹H~ê8¤ÓâÉçÅç÷ÌOd&KpÍö {¯²ØºS<ϸÃ|ò/á‘QÑ!¸[”àð« {öŠÏuÁ¶‘¬0­"å‚ú¸?U#Àl7úׯûëVô¼©Ï»H§NHU¬¸¼º´–ËÛ|õkz¿wõmçÀ~„a—ùÐG>ʉ¡!^|åe4M#UHÑ+ ´‘YZÂghRT&Dûpg"ÁJjžƒÇ…æÿêK/g&Uó/ÚHd%âUúx¿¡'D¸TFÓtZ.ÙÄÉçO3xÑFŽ>ÂÐã‡øÔg?Ë}_ú?Å­k¾W?Bð£žZèï~ƒ÷£õÊr@®»¦Û%öüßÍ!%2Up²^¤wÅ9’§d–fDÓÚM©x‰ø}(¡oÑäèîm;Œá‹ò*ÕSsêåìõ>U±)ƒHìÞSÙ8ä|›ÞføØÕW]^âå«å¼'„ÐïkšÁ¥—‹<ÿ¹Ç,ºÚ…z¯1&qY .½\bt\œö/= ‡_³¼§­Í*Ÿü„ÁëÇ îÿ3øÈÇä2 ++¶—×§Ó‰¸tÁ¬)‚Ì µ]<û^’A0*PôUbŸ€¢#óž¸P¤ìySžwPÏ ª¡V³S-Ñcxhˆ\v޶ |õkrôØQ~ñîàòK.eÿÛ®#«¥¼èœÉçŠ äìEìæfÆ2#¤²YB-6åñ4‹“3Ä ‹·¼e»ÉfcÀDKj¤²Y‘0kKö)C<ñèÃÜzí¤ a47^s ßýû¿ãõcÇøó¯ü|íËu5𦴗<ƒvÜ5z—tsý "UåýçÒ¸â—é÷;×)¸·ûS0Iiöîç®Çª{qOw/1 h”í]hé ý=Jè[DHïÂúz$ž¾N~-²®€í)ðJ‰¦&h”lŠu2•ÿökð7«°’2üºÄ÷¾#±©ÆløÅ_=8ŸNC^—F…ñþÉŸÚ¼÷}6šfxþ÷ÿþäácc6?úÜtÜýƒÖf•¯Üor÷Ç$â †Çú'áG?Ð9¶d{ï/éºÿÛ›_Ò½=¥²F.oS*}bÑìF~·!¨Æôßü$`5µÜ´®ðDMdM›hDeee™C/½ÄæmÛ0 Ô´€HQ2ú2v8C\j¡œšae®Lg"ÁLªÀÄ Y­ÈðÜXéγ Ã3fË0ª =@íýÜqÜ.2(FÄ}”½hé~¡ªÃÎoжoÖý¹Èºµþ¾Öc«Kiþ×í ÁeûÄï/½f2>jÑÚ.rÜ”a›ËZŠxô‡â¾GÙ”1å!Dä¿ömã ßûn†pÓ~ƒü ÁÖ·¶‹ÿá#¿âî/ c2aYÔö'Æ4Í «­DaE®«3ÐDšfðè• NZ5‰?~àN*–$ßwïÿ Hÿ¿Ã8ù0Vù‚±8ÙŒ"¦ö:Ç+Ï?ÃÞÝ»Éf²,,,1–JñàÿÂ{Þñ.:7î$5¡1ØÖʉ“clÙXpѽk'×\ÔÇ¡©q‘NlŠ0“*0òÂ)º»ÚíA7=Õ¿ IDAT0b¯9ÅrÎÆ˜ ³Ù²Ádc[7Ã'N26>†Ú²9K4 ôq#ÃÐáGùÕÏþ!¿ÿ»ŸfGÏàª&"װݲžKúQ›û»ù¿[)¨‡ܲž+ìqoså¾~ÈïçüraW,Æde°m“¢u Yeô’côþ|^]G`³žaëëÜæFά ×·ÀmïDœ8‡‹ccc6ü#LLUpo·íÁì‚ÏHÆG-ú†Í-¢ÿò²@/¿hÓÔ$òøÄ†(Ýgó oðêQƒ#‡l‡³Ÿ×nEð nEÀ=.¸8Lk—IJ-UµCEi˜Ë(,çÅõLn Ë0<Öß\g –± uU¥çguœwàz7ÓÈŒÅ)çÅ¥ß(Ñv/‡ž_)rbXÌíS£ìܵs±ÈWþÇ—ilLòÔä„í‹Ã,†‚øº*svNÈ/ã¹8kŽÒ¢ÄÁƒ£HKll´YYNÓŒqYw³KbÙ‚@ɬ°L`s3MÁ;z=ÄQeä/LÑ;°‰û¿ýÏüñŸü± ´|üóòy×øá¾[ô#×è×BUµ|±_Û‹êµßÏøË„rÀ©"Ä/!¢ÞÉRáS4„ÿ#ðN¯—Þ…øú9¢¼þo[‹p ;,è w¾ž{Ì"†öhŲ0ìxƒÁðëwžz^á ‹Ü¾§Gñ:ù\GpäÍ–øÇrTv‰Á l~ç½â3³Ù³Wbû Ìÿû×&ïû}‰{ŸVH;Üô©cK%!5lZ“"•8u\ UzGcà ²%‰÷ÿ¢h8hHNV2L§ÓO·$Oø#¾KÕ#uë"@]{s;׳i¥å|u=s1U¤¥3ìåÐnåÀÂ;7vœ¹Ôc‹´líavq–‰ÑFYú÷ òÜóÏ æ"ÇáÂC£/±² ¢ÿÈÔå¤`—ÒÂȧG4Î.KìîÛK¨Åæñ'Ÿ¤œŒ‘¤±ó£\ÐØƒ”–Øl b ›²)-Š/4•™ÀZÔ9}v˜‰ÑþüóŸ§1™äë_þ .½jÙÅ”§ê«E.PÒËk6 ½À煮ƒ±´ ¨Á¦eŒ´ ÿ=ÍÍ¿M¨ó£Èádœ%¡ªç¨Ãu£¼þaþZÎB¯óœQ§· µÉëNDD ‹Ä×É„œ<¾5)nK¯ˆÅc\îþË,JAø¹9:ô¿ó*Ëi~]ⳟÊð±û%^“i—EãŽ{¼>,1m ç•`9oó£èœ©| '‡M‚®¢¶½X† ¶÷žW´"E!‰‹F He¸¥7®éè+Díÿ@׳UßùT¿¾â®©­‹]î¢wãF|;/âÞ/ÿ÷~ùK|à®;yô±ÇyyäunÜ¿Ÿ×Ž ]ÿk?9ÈÙ©%º6 ãwáûÖMÍtmŠP,ÏñÒè+ôoíg÷¥Û(Mé4Y"ww·w&>}’­=›™/O°¡'D$Á^n&e:½ã£~ô½§´t’ ö½_ûàHMÌRÈ/#+ñªš¿›çû¿øg½ €Ùëå÷þë.:¨­(<Ù|+6¤gš(Ì,ÓÉÑ êkByýð_]§Äç¯ë×ëØ«G®ˆQ„^Wÿèj—žÜƶ+~u•ئl蜘@/dÈeÒ,9C.“&[(2vv‚›®¿Žlk€Ã'ž££9H¸­‡ž]»Ø±sg%2æG9½ “ÕŠ^4Ld;5ƆX'Fƒt  3ÚÉXf„lNDæ¨ÃOfµ"‰†-φâê³TÏœuȬ$½­zEÿà¦~¨_[\/êW¥SŽÀåj#G•Æßá +F¢Á ÚÑHSGšhG#¶Oš»”_Ý_ $\h‡Î}ÒfÔ‰¨µp áÀ}·µª£jÈ1öéYq¿å¼M›t®º>ÀßÜcÓÓiòG·ZìÙ+H¾¿øcqß?øc‰ûW›ÖvQïïm¹½Š˜-°BeÄWOÂË/Úüù7¶)xóÿz¢x€ã£ç&5ýÍG®ß òžÑ»°_·$ïü¨ ©®$ÎÛ,ÀŸ) Øî´_¶t† *Ý^ä÷¢•4M<¹-W}”¶ö6¯ glt‚m;·k„îûêWÅ óôÓ$ †Ÿà'=ŽÕׇž7é\fxdˆW$в‘-ûöszA†Ò ©Ó¯Ó™“YFì ‰H˜Éì(¯,ÙléÚAGg”®1ZÔ.ñ2ÝáÚbIe&˜IhQ)v–PcBÝ*MQQÈÍ/Ð=¸ÌeÁMüè›?`vI_%î)vU›°Ÿý÷ÿ¾èñÂ>qO=ÈX•ر5ïxƒ6Ôÿ{¯Ö¹Ý}gESöϽ]%ÙX1žZ¸œ5ñr÷ìBÁ«Åã{ž bÚÎÇMªûÇnÝõ+&ßú¶ÂG>&Jyÿíc¿ù9‰»ÿZbÈp}~VðKxõúÚÿgrÒàÙ§ªçö`|Iti¥7‚z¼¾ˆn<²¯"öq>;70T,C­™›X=ùMïf—tOYkü¦ÝUI.¼Ë+« âfœr±TżúÊ+<úÔ³|õ_þ…½Ñn.Úµ—©áFBr˜w¿ýíüÛ÷cò¹aNks4mk£{×N'¢§ÈjE6ï¾ ^9;D8ØFk°×Ë÷û{+Ë3›‚1¶ölfzºDXj£4¥cö÷‰DÈu&žÆiO¬Éæúó~ú»õ´ní¿û_«÷÷^~P<¶0³L‰ 3oçÊùºÒ\ýŒ=$àþ)Ûæ—¯ÿ_côITÕö ØÜ¶ß൧ ~ÿV‹¶.›åb”tZ›KêÀ`XÔæ'S2³¶ÄRIâöƒ=‹çzæ'2_ù’@Ï>mð½Q…„,^ó°ͽO+¼ô,Œœ0HÕ¨ ]'Ó' ˜ÿ7U¥êÉC®“9ïezÖ&tŽÏÊKÔ BÁró/¾3Ýç°ÝGi¬Zfˆ9ŠçSø3s‹3“ÈäYI½FÙ˜ò~ü@–*‹>Ú/¼‹Æž 0õ"í-íônÚ¼ê9_;tÈs ¾ø=}1úb]d²^;|cÏæäðK\¶uzÞ$=TRšŸ325F—ipé¦ ÈŒ¤˜Ê¢3‘`Ó®6žŸ:Ìhé7G ]ÎÓß& uerŠ´¡{ÜBy<Í©‚H zÔ6,£XÅøÛúþ^ÿïkJ(Õ²_×øK}u£¼axH ÚÑXùãÁØ9ɽ7JúË`Y$®¼\öd³½Í•—Ù¿x—B$¢Ð7àâþ"£Bmwç:˶ù,Ø‚'p[vÿê}:_»×æ#÷8ÃL5ƒ{>Ÿ¹_´ï¶6«^J‘rr"÷ØXØ‹în*â')Õšë«t ’Ír<^!k? Ýs¼5)¢ÿI¹TPtï§âÈ•UŽß2ŠçU?+¹И°ÉåÅ €`C¯§ªß lØ(ˆÁ³u§êú×~âÃàêk¯ã­o»Žæd#¯;FhSQƒˆÕÂ[nºÄ3æÅåE’mA†F_bëàDzâ iééàÂä¥LH§™šX$P(±I…P&g/’SEqÇÎh‘‰y‹Óéa®éŠòòâ<2yʆ¬ Ð_„Ê|@ö G@ußDíÈãjÛXò~çšúcè!` ¬0Z4ˆ·CaF €F5\÷$^kªÎzü@-Ä©6ÉdÀ«ÓGèºA¥9—Ñ>•YÓf|¶nR)Ç–l6HwÞ¡Ó߯ò‡¿k1=¯ð•{³|ãD· ôöˆ×ì ÛUU¯¥*6%£FZ¬Ø`®Ï}Ô¾/רù±ÉdJfƒ$­ùY¸ÛƒU “I /Ž1W—%I¥X’€Áêìüoú™9€tê„¤ï¸ØV'1tc1M´e·û¸Q%Z.FM6Ó]ç9‹Ù¢'!ž›ãÒ«öñÃÇ^åè±£ì½äR2…?xðë¼ãšwÒß»‘lʆèÛ·ƒñï•XXååÃ|óÁß!Ð’fc¬]ocìÔ¥E‰mWì ~MÇG_bbbh¦3ÚI\j!›/bÚl©q yKåefZ7 KÓbŒTð†ƒºÆîÎð¾?ÿ_¯`X1T„¨G 6¯Rþ¹ÄŸ¿PpÃhÑ 0³,€ÔH<ˆ·5G=‡Ñ¿‡à?"A‰¼\g.I^»nOHr¯¿ZäÒËÃhš˜Ó6ZÙæö“OJf9m³g¯Ê¡×lîùœß@Û82¦ð˜h²Õ 1zëÕ£@Ç èF½†ä½és½¿q'‘øßOËÞûXoˆ‰$Y±CdMƒF+‰  P (:¥rpȦX’‡lÌ5Ç}?“Àü³S"4ëîÉ^X› vív“?úŒhæyùY›G(Ä<³¶Äþ‘¯»9zÉ›óŸË(<õ¼˜ÑçÖçÝé?ß~ Ë}ÿK\wÿ¦Ÿ£’Q/ èt¤½çry;IJj±uð4&-b1¨µÝ’D w@LvDœŸÕÓ©úü_á^züÉo¶±ÄRzŽrf¢9T‰²1…,MQÅ>žGMô°íÒ÷¬*ªÑJ´Ëf²œ™mCÓ4š’I~øÈÃLŒŽ°eã êÆß{HˆÄ=9ĉ“c¤iró L;ÂԒ͈-Êf‰x™©‰"ù¢ÎÐ ãdŠÿ{_$ÙUùÝ·äË=+k_»–VWwk¥%µ„’Y`{l3Ãx áñÄÌxb0àP`F{Œ¶dcccc¬A#!Œlm-¦i­Z­–Z½©ªºkϬ%³*——/ß6?nÞ›÷½|Y•-ƒÔKÞEnÕª¬ÌwÎùÎwÎùÎî+¯ÅÙÂ,¢X7¸úðÞ¡}ÔÐò UÒúÑÕ­¢dÕ|ÕcàÌàûïGÛF#_YЬ̓jC?F•æŸ=Õ0Ì´3m ³¬al€^¬[±ÿ­òâÑjµýÓÓ&Ôhñ¤…?û/ÀÒ¼¾¦ðŽPøÁƒ.nØ'ã¶é>ñˆ‚§Ÿ«¿§•u/½Hëýsëàê>€·ûî¿ÿæfCÂ"÷£'xeÝmº¸s»2§ÈlŃRÁሤóƪ‚ªµ½%:Ov"–†.T/j¡jЃI\9qq!? 9jagR„nÒP¡›.tÓE„ùó¡äv\ù!…ë‹®ëøË¿øŸ€#Ï=…‘Þ><þ£ƒXqJøÄ;~ƒ¤¯z :R)ì¿özdÖWÑÝ7Œ"´h$ɇ‚ÊeÚ©u¹XÍÌ£CqpãäÞ\Ô¥"5@K“‹‹´!nÚÅ »ö"‰B‚Y y¢½8ô³óÏ"»ØÞËÈ?¿àvÕ¹æ\ذMO5Œ¼êàK¿®á?½Ÿ.±HÈÍ#þv=ÿ~€¹(Ö·ÿÅ/(Ø3ýý¿r‹[÷l¢§Nç]³OÂý:øÐ?|ÒE(êr†t\Â𰂃ÏKÕjö,zû[Œ=‘àÈÀ €ï)áßmGfžËßÏÊ}2–!Ûb@=¥ë u¹²OÖœA,†Jµ„Ð`³ïï§©ô¦8×Z÷°Ý,¢e2sØØÈ!öF8ÝJ• ¢ê®ùeZ~[_AHñ~%¬LëìAWOŽ;—ž9„ùÝÆfö”^=‰{øGtEè8¤â $c´Æ/uX¨¦bÈÖ–l$"aLŒîÀF>‡è&FwpÃWåM^R4L\­ÿ}¼ûQR¼3þf æD§Ð¬®/VØmHB–¸)nÖ¡äJ¨‚Ý]&Fö­aò ïfõËb„d)@q¡E]UÁo|Š2ôGP½»¾¬àŸˆãÐs.>õYàc_î;(sÞ “yÝÌsõ[ꤞȼûûš«™-pj‹‡Èô—Ì5(8‚”ü#Äz¾ÍâDûf°~vR'å±X㛿ûrñd'dm¡HÜS:~#Úß40õò D Õa˜×Zf«kô#f‘Ÿ¡v!E¤â@çøARMÏ+/E&Cë§Ù³ißè! 4€Ó¶‰wþ+*ì1œÇÉ'°:?‹ÅE ‹Œï¥uòçé¨ìlf±°Š3s‹˜>; w8‚D$Œ‚^Á®Ñؽs gæhõb¡2ƒ2PÕŸÿI>ñ¾82ìo ò–‚»þüºâ­¿5Ø4uÄ“ ƒ6&&&&jc±/v!Õü?T&KÌá·ãü¯© Ós›2þ©ÚxïÛo Žôê·œ:Få·¾òçŽNUè"+;½W<ßà\O¥±Âà/ãµÑ·Šî­ôBpÛÅ2d÷†_€ä>Icfý;],OtŽÎ{~ž ÀyŽì¬Ê4ôÒ*ê[€›;þ‹&8|𢩡…[Ð7C©,ÐÈÏJ¨ñ«º‡îÆ14؉îþëQ‘úšþžk¯¿шŠÅ©“Ø‘“qè™gðGw}£Itv§±8[Â|aý!\yÝ~œššC"†U#'hßÀF>çùÿnšEA¯ vùn¤kµôîa nÚÅÄÐvG»aš¾EŒì"ÙD6Kšvó ÏlzHCž6¨¯€ÕÿX™ª}¿ÿF‚k®Ýþ‚ Š’ |ÿ¤EŸû•[l¼ýgl|ñ ®ÙG?Ë;oŸúÓZ_À0Õj€6]¾ÁZ~GÇŽŸ½?ôt}ÏVÒbj‹†ßìßo›®’ â|ÄypΠ)#1°ѾHd•,пÓEHÂúÙaŠ }¡XÕâJ¥ôÒríÚ·<¬?!2——gŠZpìù‡ˆ?eH`³´†êæw° ”´ œ(d¯ÉÉ=Øu}àïøö½÷!PñÐCOâùÂ<ð§@Ÿ˜vsȼdÑùûbú°‰·Ý¢qvH§#/ºxïû˜¾¹ï Œ²ë¢t)ÇFÞåõzÊ›Ôïßý3–‹»@»û Á ·bòÍsˆúf»ÏnϪ6Bù“Ø5F¡|!S®|½ÃïÕã»&ëQ¿Z âÎúº`åé/¨–Ѝç`;EŸÉYMkýL(HÇñ¢ª@.Ÿm Y{«eæ`ož¬‡þ"7ÀsãW vž:yË™ FvÔE@× ¦æ¡YWã­·|ù\/LÑœ?|Ù•ø7ïz7ÖòkXœÖQR$D6"PåMtz¼¶@TZQ1{æ†û0¶kŒ:Cƒ¤„RBÕQÈ?BTX<œ€¿ÈŒ]TnVö“²ªmûYOO{sëgæ¼rTÆÐI¸ŽŽÆš>é]ré‚„L!þý_tñ§_£St_ÿjøuà3wÑû–}‡žs¹Xf Ý„ _¢«¸˜j®x:ˆ‹W_Ò¹úÎdþ àãw|÷x„àÊN* OZøÔgÙtÓ¢¯÷ôÑé;Íg¯Ì$ð`ñ=}+à‰}u›²\«ŠELEÁ(8‚dÏQó@‹$á8Hr—ï­;ÏÄÀN”3ã¨Ô~4\ÛgåJe¹¥ÐK˾¨è«úò¡.Ò¼|{Q;Xœ:AÖ–ç=·¸õÖ0«Ð×ë+Á"¤U® 6 jªÇ ô _ŽÝû?Òð;î»÷>Ï$á+/ÅCS˜˜ØS§§ñØÓG¡¦wáªÝ;ñðcOC/—ð³ï~;]4²ƒà@"8éâŒe€©U)¢ìdñ£CÏ![.cltŒçp’¢4„ŠÚÍ€ÄIQ`›tÏŸÈ¥®koýýRS, €oþHÆhôÝ#ÁµðB…ÿ•_ýU—àì¿‘Öê?tÁ}el ñÇúóÕJ]6Ë´H`Äfš{s Ä£ô»ï ÌÅ4ºu·Ø ™ä,T!ÚËî $z²èÞQ$% I h‘$´Hf5 I®#„E¦=ÓžB´o‰<ï·”gQÒÁv3µüÞ?Ü#BþF±Ï áϹ“O“KÂ0¶óµ@xmÜ'sm›:Ö×(RP5›•Ú‚L›zqæ tÓE2®b÷þpéî >ã#¸íÃïã<Û¬`qn?úÇÀfÅÁøeWãÌ̾úíÂJu+/ÍÁÉÓ/S¢—Ñ™9Z6L+*_¶:·ˆÌÜΜ=ƒˆ¢Á6ª 3à,òû£¾â4’zbé9‹ìÖqƒ»ÿ<'Óð*шŸƒB0¬ß<žÄ¡ç\ +ø¯·Xxäkþçÿ¢Ýyt§ÝA™y£L¨õó3ŸÁüOª¹ª úù¹ð{\ÜwPlÎaj:ƒà¶ŸÖ ÒSTË·±û%s ®íS}:5v£Ö¡˜Õ,BZÝ, KS ¿¯’JÆdæÇÆÒ!è¥å€hoPlʶóï¼'›U.Û÷^WŒ„\Û®æ:»F ¦aBw5$ÃÔ!DT‚d¸‚ÍJ?R©"°û=X8ùŠ›¹†ß³¼šÇÝõ×øø¯~²FG*…ÎÑQ¼|ú¼{Ç|øÃ·¡'Ý %G9ÄÝ.¼Zš…«Žs€‚¾T»@\ …DZÒYA,¬"ψ-‡N2#ïN¸X-Ô»Ù„ ز )€hüþ^öyД ÓC"#$)¨:G¶ióUÔ쌌ÓÓÀQ ˜´éŒ}*¥àƒ?gñož¡¢DƸB‰(èè „]Áð’rax×/x?¸ã·-ÌÌ)€ÐsÐ,ºuöµBòù!>!HnÄyÃQžÛ€ã` 8NZ$ CßD¢£¥Â²ÇkQ<âçY¹Ú8»†,¹igD~…;Ûiáoyƒòÿóø«„¨p­u~±3°Mk«ÓµÏ4dØ€i˜Ôàë&L¥ÒÚýtôïiøº®ãþט:}Åͬõ,þþ‘ǰkç>œ˜žÇ÷¾û]ì¾báD“W]Í¡¾º°Œ¥ò×HÄ«(èäª%õu¬•+H‡bˆ&º`;„·ð¿_(¨¬Çþf³jð‹‹-e ?˜ñkr= `ëÀ ‘kz^ÃgÀÄ…ø÷ øµ_•qôˆ‹ƒ?”ññ;éû|øÚ¨Ãä± wÈÅ(¡ŸAB£­Ä·ÞdáïþFÆÝß 0?c7®øŠ‚ q2İdߪmw;’Ý_1 à:º€8Ï"5ÐÇaþÆRªF¨# +ä Ìç´nÊIDATjZ$‰ªápÈψ?(®?‹•¹±:{22\“µ[eI†DÀW«·Òå'K²GFï’spúðÃDœw÷GÁå…“žÒ KتF/ƒPr“WÞè>þ8¢á0"‘Ö ¿ôáO"3·„¨T†®ëø³?ÿ3 'Æ1£q¶„joÑ!œÉV1{Ú@1^ÄÐ`ÊN¹rƪÔ@z;,8VÅ£ö+Nþ‰$ l›A,÷gFﯰ[Bdnô, ð§ÅRc pÓˆ„tšFå_¼ƒàŽ»FǨS¸ëË ŸÈØ”\ÉW^míý/¹. m%~ÿû¨µ¯¬›8sÆ­÷zÅ7DˆobûVãfdž¹E P2×à`]}Ï`rø=YHRÝcu DU#èÜ CßDw„–b‰~Äý0«Y˜Õ,ÔP/¢}3([¹¹…»±™Ÿ`!¬Å(F¯4°ý¶co›ë¿Ùç¼t ˆµnñ¢7«ÖV§a¦‡På w Œ €¾±[†wì@,’„®ë(m,#sæ)Üóøüåw~Dóü™9¼¶xrŸ‚É[?Ò±ŽÉT.Û1 ]ZÃlvÙr3Ö&†Âã(ºHÇ:6–V ­•!)a2›ü£¾X –UÍÓÝÇ"ˆØ’nô®kÃq"s'àX–GŽ‘€wÿÐÆÇï¤QyPñËtøù_¨ÿlOŠªæØøá“.æÎ*¸ÿkL†i9ðcï§QrtŒà/¾I‹)È Ë;‚d³·*Í©h½;>RÏÅ ¾€Ž¾ºrga¥×û™:ú&¢‰ÐÕA¨¡^” Ëö›öV羃õ…ǰ±t›ùXNªføužÅÍi”¯“|âëA†ïXVÀÿ˜~ùIÒvNÀ¿ôÖV§Q-׆ˆÃgä ª©P5©80²óg0|ùmœÜ\_ÇPÈA<™ÆìÂ:º‡wàgßûzú©cÃß?ò 쌅¹ü“ˆõÜÌçÖ UÃAo4Š|±&¾„—j»ºÆ!K.`ä„ÈlzRÅ¿ Šþþþ~Û48Hõâlù ‘y ×Ä&Y#¤(\—nîl½pb‚`’Ô!þ†^¦[ɯ•ñð?ÑŸ½ùF*Îù‹¿L;ô>ùqÚñwÍ> ¸¥ýŸ|ƒàs¿Œ ¥»siÜ ŠðÍ: ©pÃg¤ž,@Õêö“꣹´i¸òKr…|©° ³šå†Ÿ[™¹Ç)£_«ßSÃgŸ·R3f–ZÕ#¾ßÈ·‹öŠª4üÌÌ+H4qlß½8;àXÖsY˜†‰X¢mU¹Âã `088ŒÎñ"‰àð /àdÅ„mV «a,e2øÁ'Pج§®Fúª·à·÷÷°Q(@î£_ö°Ú‹™¹%lVìë =óf/•O"W-!±6Û!µÍ×}®8à‘ß’šÏˆ¢ ”Ð3ø&`¶ôCÌýYäfeI¦+ªõS0=M›{˜vƦ;¿u;Þ$ÀÛn¡à)—¶öŽS­}½êb€^ÞÛ Â¿žª¶Èä3˜Ÿ1s¼„Ç ¿°Ò IJÀuó泈¯jŽSð<Kô ©ãæÎüV澇͵ÃPðÀxo:e5”÷X~ßÔÀš˜´ãÒ×áwöØoü„àÄ`€Ú gõ£˜_¦€a"Ò<*W8/04؉þï@o_/Þsë-µRYó xäÁ=ý÷Ý{>ûëÅ‹Sëøüí·ã¯¿õ¤±]Ð¥5þ3“{)¿ g ËýèˆÂèŠB–\*.) ÑßÚæSjìa€( ÛEhê¸ dÉõ\¸"8ýbýñô´‹Êr˜çäa™Êr™¡eº¯ÕÁ†A7á~ë«4LýÞSåž—+xûÏØêtnÑý0-8qå8˜¿gð=YžßKRÝ;* 5±SVÒcõ|ä RÒ2/ï• Ëê/} ‹Ó߆&MC"à0_dòY¤¯—ï,OÞ¿]¤÷¿Î‚cYT¯1yÓŒÿ¼+nç.Û÷^—ÍÁ‹Ü€Ò°žË¢3 „¢iT«F i¨V „B%å G'°¬˜¸ótáàŒ¯üù½üþgnÿ $kŸüoŸÆžþV3óß!Hn±<;«#‰Â0«c÷Gz 4õе¿SVë%QFŽÚŽÍ/LùÅÀvdÉ…©>½¡ØJnã„BxÀª TÄb ößHðÙO[x¥¢`”Ðé<Öð3:NÓ†çŸR8’0›tARââs„T ¸á†çX /ÑW/á©¡o‚ô XÈ.äy„7 ±¤ŒRaÝazbÙ|6žByu…Â3ü³ kqX¡†í6￯£-±x*°m„%T˜ULaMSçÍboì¿à€èÄœX¬TX¤œ_õüfü%ðR¡i˜èÚ’zd5ì‰ú[H$‚ßøüãÌìþï=߯åûo†qØÁØÈ ÖÌ<Žåç±f.òÇRù hô,%`ªAAFÏ/–Úl„X5«4BÄQêÐ^%É6¶˜ÿg½øll÷ûéÆt>@×-¼ô¢ƒ±1‚oþ-ÕË_uݦ};QQíMx;õ:º Ùs”—ðDR¯xJhÙÅ"§Àa¾i¸YA©°ŒX¢N?òù§°±ñ]¬Ì}…Â3”0ÿì¨a[ ñÐ+¸jyÒ‚fQ_„û/Êß'‹ú¦©¿©‘ÿ‚C~$฀ 4Hdm–ÖP®:ñ8XD yÐÁà@©øG0wö0¢¡í;´˜£øüí·ãKø‡F`¸¯â²—£¬›0K6*Ž ½PDº£—æÞV˜jÕŠðßW l Ä1iñBdˆG–äPÿ4 þrPi³‹L¿¸Í®‰uƒ¶êÞýÿVÖ©Y²ÆœŠ |ùK>/á×@Ñ@T£ˆ!,ÓIÂV'óüNÁÁ2\,#Yƒ÷@‚çî¦ázH½ÜZ©îÔ"I„4 ¥Â2'Ì›yòù§°¾ð*Þ kqÈ!¶ãoÈQ< ¾T¼N u¸/¾iê¼·_Rä×VÞYÿ‹ˆNÀ/‰->¶ÌççP­(é„r5GP'ÂÔ1$☘¼EóÜ|á~çwðo?ú1<;· ilï8ìëìF¤FHª!™}Ñ×lˆ_ï?Hœ‘ƒþÈÏÓî4vé(d¨²Æ‰’›GŒ[Fê¦×ÿqÚИóHÈ ½eC¸ïÓØ.ç'¤B™ü¾g8©'I ÒÑ׫A,)£w0îs˜Yòy¨¡^HrÙÕ{‘™ý r wÃvŠHF:Öâ5‚4H~Ëò´èzÛu•àa[zMS‡Š@Q(ª‚³ÇóÅø/HàG~a÷-3‡Õ,ÐÝÛ‹j-Ð2GÀÒ–À2i»®oÐØ |æöÏ X)âþþ»xöб‘ÏA Eöþ1QÛ¨¢êМ°è´ëöó7Dmù½ÀåmÂ…¢†×òèG°¬Eõ,iFà¯s0A; ©Íþ{¯eq¨Y¤ß€‹2 XF¢;Ë[sY~o.gôcI¥M›ç÷¥M…üü´·ëgŸÁ†vFá8$R‚ëÆà"FlZ7x‰P¢T"´j—ÔÊ|D¨šx™ÿ È/þŒ_~Íh(ª-eZoØpÏ%ãüN@4~ÞF«O!—-!Þµ±Hý[¤÷5~¿¤' ÊLLÞˆ°Öƒ™W9§÷ñ½@WOÞºÿz”2'Ð×ÂjA$úê_ «‘-eÀªéçúŒÞ¯èw¢0ˆhü¶Cˆ€D‰§¾H›f󜼙ñB à3¿Mðòû[ÿœ©ÀuO`¤å©q{¥¯CšÓÔŒòyñ ù<$¹B!þ¦”´Œ%Ì »ð,Œê2Œ"¶.’p„†fÔuC®uÐðÔëâ`5}Dz`»&d¢BQUý më½ä@½²¤tÂ0«¨f^Ell/ODR0Ò‹¸% ŽN ¹ ¯z¸å÷pø…pø…píu×áûõu<ôì1Ì>u±xªŽIH @å ·Ö4«`ëÖ‡¤ nô"ó/>VC2$2c€Íâ  M6ÔØÏePB¦-Á§N¤ÓðlÍõÿ;Ë XF÷HM%W£Q½\H€ š( j80 —÷â3QȃÿðJE¥Â26²3ÂtÖ¡T5°§,–@]Ô¢¼÷²÷ð´–ã7;–YwÌ ¡†?÷ÊÓäB°‚‹äŒ^q«•ùüê*ÅÛŒa£ÜÝ !CLíjyEVÈØ.>òvÇ_U<Ë7˜ÒŽäæ‘îÏÁuóˆ%éû,ÜàÓ]1d‹žò»o蛼ŽÏò|ÓžBqã$Ìâ¡´PÄÛöPs¯?§ü‹ =ø«]oyóKz— tXIE,ŠŽ j‘ËÎxˆ@†ÄçX… •JcßMï@<™F$ Ô:Oüãß 5ú@W¢È ;ãg)­hØ L´¸œUDãg‚ê*M#›ää§²-#¿/ÀÒ¶3Ïoü²{½/¢£o®›!(¨”Ъ†ƒ&!·V‚ª„4 V5…ÞÁ8 }Ócüf5‹²þ 2gëÝzŽCX‹Ãq½Ýï i®/~þN¾sÀ¼´'Îg(”Ø»ÿ¢HüéÀø•·ºlˆFôÒL]hyá$ú‡v{„Ä*{ž:‚ ì½.ÓÇ#¿|¢%RðÞÇŽã]6…Úù¹]L½§~¼ ȬP¤¥H€rìß;–ÛÏ.lÈHÊÍ¡z³5X[5òtB*pÜ<ºúfx¤¯V5×Í#š(ðx‹G(Œˆ=°í9äÖ’HÔÄ #æ"¦ÎDÅ8³š§Ú‡’ ¹ÆæÓ¨/6åˆ9}½¬Wïٷиq×jÝð]ºfå÷Ž‹ó¢ŽßNs"DóÃë®î Ïü€?`¨ ZÎÈqÌMÿKÓ'[üýïØr]W+Ç_úkuY„ø·ŠŒvBûyÌå/ÃPÊÆÂ† )z-bÚ ¢ -¥¢šnX¦€ ºjÓwJh¦á¢'Æj1 %´Áa},)#§ªTÔyžÏr{±ŽoÚS(¯. bFÅ(BU#<²»¾Üž¦,É<í ŽmÍ?»FÖ?໨Õñ/d¨I9‘€ŸIgHÀýýiwg·T&ìè߃®þáÀúý¹?‹ô¬Øê¡+Ä5zQùw5è¾IYAQ»£](,‡[&þØë–‘îÏñÈ^.$ „6`USèŽW°Z £gÀBn­„t]ž²²¤ðŸg]zâÉçŸBqã$Jë/{µÍÚtkFÍÊw¢ fýqpžïåêýÏ·šf^Lç¢u0qÕ;Ý è/kƒ£Wx ? ”jKUMÅô©çŸ?¶%9ÈÀO"úûK›Œô;ÆþÛŽÍó^f²$Ãp.©^†r¥ýÝq¸Ò[a£Ûˆ/>NõGÏ@ý÷†"“¨ê§],¢w0ŽRQç$ÿkÑ>ÝÃòü‡ø,È®Þ }ý8JůEb¯žË[DŸ/À¹µé ‹ö<Ë9.Fÿ$€ Ak1hæªU*=æÚE9޵Õi,ž|Ê“û‹Õ-5úº@3ã!¾øXløa“€"\fNÀÂU ÕËhÄ®t±kÑŸðL܉0ß4ó»úf8¤OwŸá3'°0MeÚ˜s(량˜ëú&ú‡°0ÿŠ'Q-žÁ&,'ó V˜tdF^‡ðʶ9½èøTžìLóü¯á·À9:±lÆ`2;é^Ä;šGDæ Šyª³Y4Ë„‘H²FßÄu-»Ý Ûíw²${†üÏ¥Ì@ÄŠ‚…½Ü€.½]ñ«!BÊ6…ùA†ŠLr+/yr{ÙÅǼܷ4ÃËxúæq(R Žã= ~äÂþj”aO¤»ùhÅãõ•öؤs¬ÁѼÓv¯ãì¼ê:WÔÖçšzµ–Û­œ€¿thÔdL}'ÿƒÇ D"$’ $®>‡¨ïu¢±‹œE¡éE¶'’Š ¸…«ê_zõ2¸Òˆ'öy+á¥út^ž+<²‹‘Ÿ93øÜZÉ#§ÍJ}¥Â2JƺïQÏ®wôý±\Þ[Ó¯¹×Ø_¿Ñû¡¾LT„B¦^~á’±…KÒˆNÀoXbcMWz½CÑ_LŠùe„¢i”󫘙zÉS&Œ'ÓèÙñÖ-;ý¶sì=ùŸ³œ7~Ñx!?á93‹°U§.êã´ºñ1DS7Ã5×QOc¯u’7ì°<^Œæéžk™‘“zâaÌ>{­TÔ‘ÏÓÙ{Ç<ŠR¥Äs{ñ½y.D±œà'büzóNÛüÒ^-Fß®ô ¢Ýä si-LΑã0õ ˆD"Üý¶×eøþ稱ËMQBÐ<€Ÿ „’€°¾Fç`¤zÉ¢£o†ëâ[Õö"ƒdðÌ9ˆp?b.b©4ÓóEåò¨%& ¹Èê77üfÄŸ(Æ!–ò.vb¯í¶9ƒ“ûÝh¬£Ar\„ØñX}Ã{P@PZà/F"ôï|Ç¿øo›¡ƒfA~G`:Q> @EçÀt$~‰·ßZÕ”§¬ÇÁÊ’âaþƒ€“›B!»ˆÙÜ?Ãv3P¤,'Õ MÖ,ª7–ò¬€6^k[ƒ÷¿.¢<æh.õˆßvN@Ì¡EbPQÓa‘­BÆ Ì=Œüü±sF¢QûŸ`qøÏri1G]ÅçD¸€á‘ŸE¢£ƒçê¢A¹?Ú³2Þâk÷Ò¦òd¥Z4æQ-k÷ ¹ÐNºfù¼øøÜà¾hø¶kòQ\Û5/)b¯í^'ðÃk?J‚Ð@&³‰X-­ín†ôù ÑðƒI@â1~Ñè\eF& ­¯‚í&ôO¾ÍCÖ‰ù¾ÿ¾øœŸÍgá5YƒaQžmĵ"c÷ÞëËïÙç`»&ï¬Ŷá·@kN`«®=M yÒMV`ØV IÈ_aöà‚J…A€þ{"o-¨f61zý> ¿Ù¥ºG0rñ¶TXFïÀ¸×ðן…©OÕFp#¥8\·¸åße™µˆïkëý±w> p´ÀyL Š­·¢‘¹Ö:‘~h– ´L¸¾6Ç'·«÷È¿¸˜2qúÿit®Å]anüsH "`aþ1T­èëÇQ5ŠP¤€W¢Æ.CQé"R±·Ñø½ë°½ß;Õç¸Ö–‘_"€¤*žv] ñÛà§ìÒÄíêö½Ÿp[<{ =ý“èìè€a[ Ư…lgü,¯a®?ú‡$ÅcðÁ‘¾öo…ÖZ¸5Uáj!eÈ“ç[ ?Ærø(VÃ4u„µxí}Èlψ­¿ž_ŸÎó3ü4ª×Ë|AÑß ”ï²L ˆö3ûÔàÃMIAÔ÷Ïá3Øß6ü¶8ï€m¼Å—Ý#:CdÛy›fhÀoìA‹@Ù¼<‹öb l4µõwúá~³Q\±€EÞTåƒù~¨ïX펽¶¸€ÓOÈ;5²©TˋǷõ;¿ x«À?yç;ñü-¹Ap_ÔÛce¿×Ó®Ë"»”×v¥`ŠBAy}+†¾•S`‚ÌèÅR`#‚üiAóº½Wu'È4kÕõ«ëŠ·ÍÚuÛ†ßv•`«·Ä|ž]ðÍŒ|«ç4hÿ‰†ïGA·Í€ß94Ë×_OÇ^Ñ6ü¶¸¨“÷ƒþο ƒ÷ªýûÅ È ø#¿ÉýN¡™ñCüF¹ííK#Œ*­á³Çívݶ¸¨ÏèïrE¦?h~Ÿ×Û›´‘ÛI~E†m4@÷­4õEƒo6•×jÄ÷—ñ˜‡ã¶ý¶¸ÄÀV#uxïm ê3Úþ[WÒ… ”Kï3AæØã Ptç"·åŸ»g†Ï¢ÛðÛà’åüÑœ5ÖlEþù7ÿ4«° $)Ð-;°ñÇñEçàWÝõCþVr|q™†Ø®ÛŽømÐ>‚w°Ç¢#ð {ˆ=ÿþÉÃú"ÂÇ}Åžÿ û[Mê5«åo×ÃÏÊxì(ªÒÎïÛ }ügâªwº~È.«oÚ*ßo¶ ,(hÖìTöó¦ ­åõ~ØÏÞ ÐfôÛ }Zv¬ý–¿ø|+¤ãDÇágò›ÕúÙó,z‹y[MæmRn~Û´Ï9œÑ+nu#ŠÝ2 ØóEN h@Ü$®Ð ‚ýAõ¯f@0áç_¢€³úmÃo;€öyN@ i\·/vûˆ?êûIDöº8«ß 8.Òaüõûhp6þ<¿­µ×víótþü½™#h6éçß +*‰‘>HkßëýÑ_\’©¨J»”×víóF:f†Dò‰¯;‚&Î…xÇzÅ>}¦äÓ6ü¶hŸŸâa›‰ÍªÍËy~C÷~1›p‘ Xùݨ³/"fõ|ÿTƒúmÃo;€öyƒÎàä~7é€nh%-Ó17Úi~D-ýöæœöi;€7Ë ìÜㆣ¼_lâa‘Ýÿ¸1ºo=¸ãWß1ª:_”Ù6üöi;€7ù¤öºéîþ6¿±açÜÇrý]{mF¿}Úà<={®y¯Ë~™ñ³ñq]Ø“Õñ•†Ÿ Vémké·OÛœ÷)¬t®-ÝêÈD þ9»È þP/‘[:ÞþnÛ§í.”sÅ l(âéå(äP ¶ „$UGE$B ë.d)Þ¯ïØóµ¿Ïöi;€ ý$Rg Dû`•3ül³‚r¹ÜþþÚ§}Ú§}Ú§}Ú§}ÚçÏÿ¾a¥Eg®˜˜IEND®B`‚CubicSDR-0.2.3/src/CubicSDR.xpm000066400000000000000000004121701322677621400160440ustar00rootroot00000000000000/* XPM */ static char const *cubicsdr_xpm[] = { /* columns rows colors chars-per-pixel */ "256 256 256 2 ", " c #010101", ". c #0A0A0A", "X c #171817", "o c #0D0F12", "O c #221E1A", "+ c #28251C", "@ c #111929", "# c #1D273A", "$ c #1F2B3E", "% c #1A2635", "& c #1A2328", "* c #272828", "= c #212C3E", "- c #232D33", "; c #333434", ": c #3A3E3E", "> c #363A3A", ", c #2D302F", "< c #2A1D27", "1 c #463B37", "2 c #3B413C", "3 c #4B463A", "4 c #64603E", "5 c #1F2B42", "6 c #1D2B4B", "7 c #1C2B55", "8 c #222D41", "9 c #222D4A", "0 c #282F45", "q c #243145", "w c #25324A", "e c #2B364B", "r c #383E44", "t c #283656", "y c #323C58", "u c #222C55", "i c #131268", "p c #131478", "a c #091173", "s c #1B2B6B", "d c #283967", "f c #2A3473", "g c #15145D", "h c #4F394A", "j c #3D4243", "k c #3B434A", "l c #36425B", "z c #3C4C6C", "x c #374567", "c c #374974", "v c #2E4473", "b c #414646", "n c #42474A", "m c #444A4B", "M c #4A4D4D", "N c #474846", "B c #534D4D", "V c #594B4A", "C c #4B504F", "Z c #454B51", "A c #484C54", "S c #4C5252", "D c #4D535C", "F c #525555", "G c #545B5B", "H c #5A5C5C", "J c #565756", "K c #57504E", "L c #654C4C", "P c #675656", "I c #765656", "U c #6F4F50", "Y c #5D615C", "T c #716957", "R c #424C6B", "E c #595F60", "W c #545969", "Q c #425273", "! c #495577", "~ c #4B5370", "^ c #6F5668", "/ c #5C6363", "( c #5B666A", ") c #5B6679", "_ c #646464", "` c #636B6B", "' c #6B6C6C", "] c #686666", "[ c #756868", "{ c #6B7373", "} c #697577", "| c #747473", " . c #737C7C", ".. c #7B7B7B", "X. c #797776", "o. c #6F6D72", "O. c #8D5354", "+. c #AF5254", "@. c #8A7370", "#. c #936A65", "$. c #D05456", "%. c #EF5557", "&. c #FF516C", "*. c #FA516C", "=. c #D46768", "-. c #7D817B", ";. c #FFFA01", ":. c #FCFA09", ">. c #FBFB16", ",. c #FAFB2B", "<. c #998976", "1. c #918773", "2. c #A4917B", "3. c #8B835E", "4. c #F7FB4E", "5. c #F0FB6D", "6. c #ABF975", "7. c #131788", "8. c #131994", "9. c #121B9B", "0. c #1A1C97", "q. c #0C188F", "w. c #2C328E", "e. c #121DA3", "r. c #121EAC", "t. c #071DA7", "y. c #1221B3", "u. c #1222BB", "i. c #1D25B6", "p. c #0F22B2", "a. c #2A31AF", "s. c #1C258F", "d. c #34488E", "f. c #3748B9", "g. c #394BB1", "h. c #4D5292", "j. c #596489", "k. c #6E7089", "l. c #4D53AF", "z. c #6868B2", "x. c #625EAA", "c. c #1224C4", "v. c #1226CC", "b. c #1B27C6", "n. c #1228D5", "m. c #122ADB", "M. c #1327D2", "N. c #0A27CE", "B. c #2A33CF", "V. c #112CE4", "C. c #112EEC", "Z. c #1B2EEC", "A. c #0532FF", "S. c #0C32FF", "D. c #0C38FC", "F. c #1232FE", "G. c #1C33FE", "H. c #1332F7", "J. c #0F2EEE", "K. c #2633FF", "L. c #2B33FF", "P. c #2734F7", "I. c #3336FF", "U. c #383AFE", "Y. c #2933E7", "T. c #423DFF", "R. c #394AC6", "E. c #3E51C7", "W. c #3A4BD6", "Q. c #114FFD", "!. c #334BF7", "~. c #0E70FE", "^. c #2371FB", "/. c #4458C9", "(. c #4E57CE", "). c #4F64D2", "_. c #6F70D0", "`. c #4845FF", "'. c #5453FB", "]. c #556DF8", "[. c #716FFB", "{. c #645BF1", "}. c #403D95", "|. c #947586", " X c #A57B91", ".X c #847AB3", "XX c #847BFD", "oX c #837AD4", "OX c #7B8484", "+X c #7C8585", "@X c #7D8493", "#X c #7D88AD", "$X c #088FFE", "%X c #04AFFF", "&X c #229BFB", "*X c #728CF8", "=X c #04D0FF", "-X c #2BD1FE", ";X c #2FF1FB", ":X c #08F3FB", ">X c #5EF2ED", ",X c #838383", " : j : n [ _ Y _ _ I _ ] P I [ T P [ O.O.O.O.#.#.+.+.+.+.+.+.O.O.O.U O.I H U V U L K L V V B V V N m M N M M C M M M b j : : > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX> r 2 j b j b N m M M C S S C F S C M M M F _ _ T ' ] ] ` ] [ ' ] [ [ ^ [ [ [ [ [ O.#.#.#.O.#.[ #.I U U I L I H P H J J K V B A N h N h N b m N N n m m N m M M M B K C D A B n b b : : ; UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX: j j b b b N M M C C C C S S C B C C B Z C M C B M M M m Z M N m M m m M B Y _ ] ' ' ' o.' ' ' ' o.[ [ o.[ [ [ [ [ [ ^ [ #.[ ^ [ #.] I [ _ _ T P Y P P P F J K M B m N N n 1 M B M B M M B M M N N N N b Z Z n m M M M M C B C B B M b b 2 2 > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX2 j j b b b b m C B S F S S F F F S C C M C C B M M M m M M M M M Z M M M M M M M M N M M M V M M B B B C M M / o.' o.| X.X.X.X.......X.X.| X.| | X.o.| | | [ [ o.' ' ' ' o.[ ] [ [ ] ] _ _ E J G F F K N B N n N b N N n V M N n N m V m B M M N M m m n N N m m n b b M m M M M M M M M , , , & & O X UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXj k b m m C S S S S S F S G C S S C S S S C M A M m C Z M M M M M M M M M m C A C M M Z C B B M M S M B B B M C C A N C B m C B B C M M V S ' o.X...k.,X,X,X > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXG G S S m m m M m m m C Z M M C C C Z C C C M C C C S C C C C M C M M C M M C A C C M A M B M Z M M A M M C C B M M C C M M B B B B B M M B K B A M B C M F H ' X.,X3X3XwXeXtXeXeXeX8XwXwX8X3X3X3X3X3X3X4X UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX, ` G ` ` ` ` { { } { { .{ .} { . .} } .{ { { ` ` { ` ` ( ` ( / / / / E E G G G G F F D D S S S S C S Z C C C m M M M M M M C C m C M M m Z M M M m Z C M M M M B B M M M A C A B M B A h L L U U U U O.O.+.O.+.$.$.$.$.$.$.$.$.+.+.O.O.+.U U V L L V B N V N N B N M N N N N n m b M n m N b m m m m m m N m N m m N M m n N m b m m m m m m m C m m C m m C C C S C C C C S S C C S S S S S S S S S S S S S S S S S S C S Z C C C C C Z M C Z Z M m m m k m b j b j 2 UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX; ` G ( ` ` ` ` } ` { } { } . .{ . . . . . . .} .} { { { { { { ` ` ` ` ( / / Y Y G G G G G S G S S S C C C S C C S C C C C C C C C m m Z m Z m C C m C m M C C C S m M M M B M B M M V M B V L L L U O.O.O.+.+.+.$.+.$.+.+.+.+.+.O.O.U U U L V V b V B N N m N m m N M N m m m m m N m m b m m m m m m m k m m m M m m m C m m C Z m S m S S C S S S C C Z C S S Z S S S G S S S G S S G S S S S G S S S S S S S S S Z C C S C C C C m M C M M m m m m m b m k m j j j 2 > UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX> ( G / ( ` ` ` ` } ` } ` { { { . . . . . . . . . .-. . . .} .{ } { { } ` ` ` ` ` ( / / / Y Y G G G G G G G S S S S S S S m C C C C C C C C C C C m Z M M m m m m m C M M M m B N M M N V B V V L L U U O.O.+.+.+.+.+.+.+.1 +.O.U 3 L U 1 V V N V N b m N B m b m m m m N m m m m m m m m m m m m C m m N M Z C C C C C S S S S S S S S S S G S S G S G G G G S G G S G S S G S G S G G S G S G S S S S S S S S S S S S S Z C C C m C Z M m m m m m m m k m m m k j j j 2 M UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX2 ( G / / / ` ` ` ` ` ` ` { ` { { { { . . . . .OX-. .OX-.OX . .-. . . . .{ .{ { { { ` ` ( ` / / / / E G G G G G G S S S S S S C C C C S C C C C C Z C m C Z C C C m C M m N M M M m M V V N B V V L L U U U O.1 O.O.O.O.O.O.1 V V B B h V N N N m N n m m m k m m N m m M M m M M M M C C C C S C C m S S S S S S S S S G G S G S G G G G G G G G G G G G G G G G G G G G G G S G S G S S G S S S S S S S C C S C C C C Z m C m m m m m m m j m m m b 2 m m b 2 b k 2 j > C UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX2 / F Y G Y / ( / ` / ` ` ` ` { { { ` } .} .} OX . .OXOX+X+X .+XOX+X+X .OX . . . .} .{ { ` { ` ` ` ` / Y Y Y / G G G G S G G G S S S S S S Z C C C S Z C C C m m m C m m M M m m m m m B V M V h V V 3 V V K 1 L 1 1 1 1 1 1 C k N m 1 h V b m M m M m C C C C C C M C C C C C S S C S S S F G S G G F G G G G G E G G E G G G E E E / G G G G G G G G G G G G G G G G G G S G S G S G S S S S S S S C C C M C Z m m m m m m m b m m m k m b m j b b b b j b j b j j j > C UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX2 / D E / G / ( / / / ( ` ` ` ` ` } } } { { { { . . .-. .+X+X+X+X+X+X+X+X+X+X+X+X-.+X+X . . . .} } } { } ` ` ` ( / / / ( G Y G G G G S S S S S S S S S C S C C C C C m C m M m m m m M m M m m N M N M C m C C m m C m 1 h m m M C Z M Z M C C M C C C S S S S S F S F F S S G D G G G G G G G Y G G Y / / E / / / / / / / G / G / G / / / ( G / G / G G G G G G G G G S S F S S S S S C S C C S C C Z C m m m m m m m m m m m b m m b m b m 2 b b m b j j m 2 j j j 2 m > M . UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX> / S G G G G G Y / / ( Y ` / ` ` ` ` ` { { { { { .} . . .OXOX+X+X+X+X+X+X2X+X+X+X+X+X+XOXOX+XOX . . .} } } } } } ` ` ( ( / Y Y G Y G G G G G G S S S S S S S C C C Z C C M M Z C M Z M m C C m M M M m m m C C C C S V V C C C C S C S S F S S G F D F G G G G G G G / E / G / / / / / ` / ` / ` ( / / / / / ( / / / / / / / / / / / G G G G G G G G G G G S G S S S S S S S S S C C C C m m Z m m m b m m m m m k m m j j m b b b b 2 m b m j b j b j b j m j j j r j > m o UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX2 E S G G G G G G G / G Y / / / / ` ` ` ` ` { { { { { . . .} -. .+X+X-.+X+X+X+X+X+X2X+X+X2X+X+X+X+X+X+XOX-.OX-.} } } } } } ` } ` / / / / G E G G G G G G S S S S C C C C C C C C C C C C C S S C S C C S C S S S S G S B S D F D S G G G G G G E / E E / / ` / ( ` ` / ( ` ` ` ` ` ` ( ` ` ( ` ` ( ` ` ` / / ` / / / / / / / / / G G G G G G G G G G S S G S S C S C C S S C m C m m m m m m b m m m m m m m b k b b b j b b j b j b b j m j j j b b k b b 2 k j j j j j : m X UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX; E M G G G G G G Y G / G Y / / / / / ` ` ` ` ` { ` { } { } } . . . .+X+X+X+X2X+X2X+X2X+X2X2X+X2X2X2X+X2X+X+X+XOXOXOX . . .} } } } } ` ` ` ` ( / / E E E E G G G G S G D S D S S S S S S S S D S F F F G G G G G G G G J Y Y / / / / ( / ` ` ( ` ` ` ` ` ` ` { ` ` { { ` } { ` ` { ` { ` ` ` ` ` ` ` ( / ` ` / / / / / E / G G G G G G G G G S S S S S C S S m C C m m Z m m m m m m m m m m b m j m k b b b m b b m b m j b m b b b j j j j b b j j j 2 j j 2 j j j 2 j > m & UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX; E C D G S G G G G G G / G G / G ( G / / / ( ` ` ` ` { ` { { { .} . . .OXOX+X+X+X+X+X2X2X2X2X2X2X2X2X2X2X2X2X2X+X+X+X+X+XOXOXOXOX} } . .} } { { ` ` ( ( ( / / G G G G G S G G G G S G G G G / G / / ( ( ( ` ` ` ` ` ` ` { } { ` } } } { { } } { } { { { { { { { } { { { { { } ` ` ` ` ` ` ( ( / / / / / / / G G G G G G G G S G S S S S S S C C C C m C m m m m m m m m m m m m m m k m m b b m b b m j b m b j b j j b b b j j j k b j k 2 b 2 j j j j j j j 2 j j 2 > m & UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX, / M G G S G G G G G G G G G G G E E ( / Y / / / ` ` ` ` ` ` { { { { . . . .-.+X+X+X+X+X+X2X+X2X+X2X2X2X2X2X2X2X2X2X2X2X+X2X+X+X+X+X . .OXOX . .OX . .} } } ` ` / / / G Y G G G G G / G / / ` ` { { } } OX . . . . .OXOX . . . . . . . . . . . . . . . . .} .{ { { { { ` { ` } ` ` ` / ` / / / / / E G G G G G G G S S S S S S S C C m C m m m m C m m m m m m b m m b b m b b b b b m b b m b b b b b b b j j b b j b b j j j k 2 j j j j 2 j j j j 2 j j j j j 2 r j > b & UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX- G m G S G S G S G G S G G G G G / G E E / E / Y ( ` / ` ` ` ` { { { { } . .} -. . .OXOXOX+X+X2X+X2X2X2X2X2X2X2X2X2XwX2X2X2X2X2X2X2X2X2X+X2X+X2X2X2X+X+X+X+X} } { ` ` ` ( / / / / / / ` ` { .OX2X2X2X2X2X2X#X2X2X2X+X+X2X+X+XOX-.OX .OXOX .-.-. . . . . .{ { { { { ` ` ` ` / ( / / E Y G G G G G G G G S S S S S C S S C C Z C m m m C m m m m m m m m m k m m m b m b m b m b m m b b b b k k b b j j b j j j b j j j j 2 j j j j 2 2 j j j j j j j j j j j j 2 j j j > m X UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX, / m S G S G S G S G G G G G G G G G E G E G / / / Y Y / ` ( / ` ` ` ` { } } } . . .-.-.OX+X+X+X2X+X+X2X2X2X2X2XwXwX2XwXwXwX2X2XwXwXwX3XwXwXwXwXwXwXwXwXwX2X@X+XOX . . .} { { } ` ` } { .+X2X3XwXwXeXrXeXwXwXwX2X2X2X2X2X M X UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUX& Y m C m S S G G G G S S G G G G G G G G G E G G G E / E / / / / ` / ` ` { ` { { { . .} . .-.+X+X+X+X+X2X2X2X2X2X2X2X2X2XwX2XwXwXwXwXwXwXwXNXNXNXNXNXrXwXrXwXwX+X@X@X+XOX-. . .} . .OX+X2XwXeXrXNXNXNXrXeXrXwX2X2X2X+X+X+X+X . . .} } { { ` ` ` ` / / / / / E G G G G S G F S S S S S S C Z S C C Z Z C C m C m m m m m m m b m m b m m m b m b m b m 2 m k b b m b b b b k m j j k b j j j 2 j 2 j j j j j j 2 j j 2 j j j j 2 j j 2 j j j j j 2 j j j 2 r 2 j r j j ; m X UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXX / m C m k m m C S S G G S G S G G G G G E G E G / / G E / E / / / / / / ` ` ` { ` { { } } . . .+X+X+X+X+X+X+X+X2X2X2X2XwX2XwX2XwXwXwXwXrXNXNXNXCXHXCXHXIXNXNXwXwXwXyXwXwXwX2X2X+XOX .+X2X2XwXNXNXNXNXdXeXwXwXwX2X2X+XOX . . .} } ` ` ` / / ` / / / G G G G G G G G S S S S S S S C C Z C C S C C C m m m C m m m m m m m m m m m m b m k b b m b b b k m m 2 b b k 2 j b j j j j j j j j j j j j j j 2 j j j j j j j j j j j j j j j j j j j j j j j j j j j j j 2 2 j > k X UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXo / m S C m k m k k m S G S G S G S G S G E G G G G G G G E Y / / / / / Y ` / ` ` ` ` { { { { { . . .OXOXOX+X+X+X2X2X2X2X2X2X2XwXwXwXwXrXNXNXNXHXHXHXLXLXJXHXIXNXrXeXrXrXNXrXwXwX+XOXOX+X2X2XwXwXeXwXwXwXwX2X2X+XOX .} } } { ` ( ( / ( G G G G G G G G G S F S C S S C S C S S S C C C C C m m m m m m M m m m m m m m m m b m b b b m b b k m b b b m b b b m b m 2 b b 2 j b j j k j j j j j j 2 j j j j j j j j j j j j 2 j j j 2 j j j 2 j j j j j r 2 j 2 j j j j 2 > Z o UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXY k G C C m j m m k m k m S S S G G G G G G G G G G G G / G G / G Y / / Y / / / / ` / ` ` { ` { { } } } OX} .OXOX+X+X+X+X2X2X2XwXwXwXwXwXNXCXHXHXLXLXLXLXLXHXNXNXNXVXBXHXIXNXwX2X+XOXOX+X@X2XwXwX2X@X2X+XOX-.} ` ` / / Y Y G G G S G G G F S S S S S S S S S C S Z C C C Z m C m Z m m m m m m m m m k m m k j m b b b b b k b b b b b b b b 2 m 2 k b 2 b 2 2 k b b k b b 2 j j 2 j 2 j j j j j j j j j j 2 r j 2 j j j j j j j j j j j j j j j j 2 j j j j 2 r 2 2 j > C o UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUX/ m M M S S k m m k m 2 k k 2 C C S G G G G G G G G G G G G G / G / G G / / / / / / ( ( ` ` ` ` { { ` { } } .-.-.-.OX+X+X+X+X2X2XwX2XwXwXNXNXIXHXLXKXKXKXLXHXVXNXCXHXHXJXHXNXNX2X+XOX . . .OX .OX .} } { ` ` ( / Y G G G G G G S S S S S S S S S S S C C C C M M M m M m m M m m m m m m m m N m n b n b b n n n b b b b b b b b b b b b j b j b j j j b j j b j j 2 2 j j j j 2 j j j j j j j j j j j j j j j j j r j : 2 b r b j b r j j j r 2 j j : 2 j k j : j : j ; k o UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUX/ m S C C S S m k k m m m m k b m k m C S S G G S G G G G G G G G G / / G G / E / / / / / / ( ( ` ` ` { { { { } . . . .-.+X+X+X2X2X2XwXwXrXNXCXHXHXLXLXLXLXHXNXNXCXHXLXLXHXHXrX2X+X} } { { { } ` ` ` / / / Y Y G G G G G S S S S S C S S S S C C m C m M m m m m m m m m m m m m b m j m j m b n b n b n b b j b b b b b b b b j b j r j b j b j j j b j j j j j j r b 2 2 j j j j j 2 j j j j 2 j j j j j 2 j j b b b b r j j j : j j 2 j j j r r r k r > k j : 2 r j ; m . UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXE m S m m C Z S m b m k m 2 m m b 2 m b b m m S G G G G G G G G G G G G / G E G / G / ( E / / / / ` ` / ` ` { { } } } . . .-.+X+X+X2X2XwXwXrXNXNXHXPXLXLXHXBXNXNXNXHXLXJXHXNXwX2X .} ` ` / / ( / / / / Y G G G G G C S S C C S C S C C m C m m m m m m m m m m m m m m k m j b b m b m m m b b b n b b b b b b b j b j r b b b r b b b b b j j j j j j r j j j j j j j 2 j j j j j j j 2 j 2 j j j j j j j j j j j r r r 2 b r b b n n j j r j r j : j r k C j j 2 : 2 > m UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXF m C C Z C m m m b m m k m 2 m k m b k m b j k m C G G G G G G G G G G G G / G / G / E E / / / / / / / ` ` ` ` ` ` { { } } .-.+X+X+X2X2XwXwXNXNXCXHXHXHXCXNXwXwXNXCXHXHXCXNXwX+X} } / / / / G / G G G G S G G S S C C S C C S C m C C m m m m m m m m m m k m m b m j m m b b m b m j j b b b b j b b b j b b j b b b b 2 b j b r b b r j k j j j j j j j 2 j j j j j j j j j 2 j j j k j k j j j 2 j 2 : j 2 j b Z M F S D D A A Z m r r : : j : r 2 k M Z j j j : j ; M UXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXS m S m m C m m m b , > m m m k 2 m k m m j m m m m k k C S G G G G G G G G G G G / G E E E / E / / / ` / ` / ` ` ` ` ` ` { { } . .-.+X2X2XwXwXrXBXNXNXCXNXNXwXwXNXBXCXNXNXwX2X+X} ( / / / G G G G S S G G S m S C C C C m m m m m m m m m m m m b m m m b m m b b m m b b b b j m j k b b b b j b b b j b j j b j j j j j j b b r b j j j j j j j j j j j j j j j j j j j k j m j j b r : : > j b n Z A D D W W D W D D D C M M M n M b : j 2 k r j k m M C j : : j j ; m UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXS m Z C S m m C m m & o & 2 2 m m m j m m b j k m j b m b C C S G G G G G G / G / G E E E / E E E / / / / / / / / / ` ` ` ` { } } . .OX2X2X2XwXrXNXNXrXrXwXwXwXwXrXNXdXNXwX2X .} / G G G G G S G S C S m C C m C m m m m m m m m m m m b m j m m j k b b j m k b j j b b j b b j b b b b j b b j b j b j b b j b j j b b j r b b 2 j j j j j j j 2 j : j j j j b j j j j 2 > ; > 2 b S D ~ W ! ) ! ) W W ~ D D C A N N N M N M B A M j : j j : j j m Z Z m j j 2 2 j ; C UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXM M C C S S C C m C - . - 2 k m b m j m C m M C m m m C m m C G G G G G G G G G E / Y G E E / / Y / / / / ` / ` / / ` ` ` ` } } .OX+X2X2XwXwXwXrXwXwX2X2XwXwXwXeXwX2XOX} ` / G G G S S S S C C C C m m m m m m m m b m b m b b b j m m b j m m j b b j m j b b b b b j j m j b b j m j j j b j b j b b j j b j j j j j j 2 b j : r b j j j j j b 2 j : : > ; 2 k C ~ ( ) @X#XyXvXyX) j.W W A m M N N N N M M B M B C K B S B J b r : j j j k k C M M j > 2 r j ; m UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXj Z S S S C m m m C 2 . . . . X ; C k m m j k M m m m m b m m C m C G S G G G G G G G G G E / Y G Y G / / / / / / / ` / ` ` ` ` } } } OX+X2X2XwX2XwX2X2X2X+X2X2XwXwX2X+X .` / G G S S S C C C C m Z m C m m m k m m b b m b b m m m b j b j m b j m j b b j m b b b j b m j j b j j j j b j b j j j b j j j j j j j j j j j j r b b b r j 2 j : : : j b A ~ ] ) k.@X@X2X|.k.2XrXCXNX : 1 n n A R W j.j.x.k.k.k.k.) W _ J Z M > tXKXHXAXF M S B S B B F F S J K F J H H H H H H H H b > b k m m n S Z Z m j : > r 2 > j UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXC M C N C S m m m M C { X.H * X : : j b M A F W ~ ! ) j.) ) ) ~ D J h n m N m N M N B M CXPXPXLXJ A K K F J J J J J H H H H H H H H Y _ _ _ b r b m n m n Z M M k : : j 2 r > 2 UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXC C C C S m m m m ; , Y ..| M . 1.' , o * 2 m 2 m C M M m m C k C S m m C m S C S G G G G G G G E E E E E E / / / / / / ` ` ` ` { } .} } .} } { { } .} } } ` Y G G S S C C m m m m m m m b k m b m k m b b b k k 2 m b b b b j b b b b j b j b b j j j j m k j j j 2 j j j j j b j b k j j > : : 2 b h Z A D W W ~ ! ) W ) D D M B B n n N N m M B C B B S B K S M CX3X4XCXN E J J E H H H H H H H _ P _ _ _ ] ] ] ] _ b b n k m m M M m M k : j 2 r 2 > 2 UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXm C C C m C C C C j X . b ' .._ ,Xo.* o ` J k S M C C C C C C m C j m C C C C C G G G E G G G G E G E E E / / / / / ` ` { ` { { } } .} } ` ` ` { ` ` ` ( / G S S C C C m m m m b b b m m b b b b b b b b m 2 m m b b j b b b b j b b j b j j j j 2 j 2 j k j m m b b b r j : : : j b n D D G W ! ) ) ~ W W D A A M n N N B n B A B S K M V C B S K S F K J B ' tXY _ 3X_ H P E E Y _ Y _ _ ] ] ] _ ] ] ] ] ] ' | ] b m M m C n Z M m M m : j : 2 : > > UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXk S M F C m S S N D J . b X.wXeX] O 2X; | H b C m S m C b m S S M C C C m C N C G G G G G G G G / G / / E / / Y ` ` ` ` ` } { { } } ` ` ` ` ` ` ` / / G G S S C m C m m m 2 m b m m b b m b b 2 m b m 2 b b 2 b b j m b b j j j b : b r b b b m m j m b 2 > > > > b n Z W W ! ) ) W ! W W D A A M n n n M M n M B B A B B M S B F B F J K J J G J H J H K X.2Xm o.NX_ E _ _ _ ] ] _ ] _ ] ] ' o.{ | | | | o.' ] n b m m m m Z Z M Z k 2 j : j : > - UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXj S M S S S M M S S 3X* . 1 | _ 4X| ..' b . . j k m D ` ) j.k.j.k.j.! ) W W A D n C m M B M M B M B M B B B S B B B S K J F F F H H H H H H H _ H _ K 4X3XJ ] VX1._ [ ] ] ' ' ' { | | X.o.' ' ] _ _ [ ..4X,Xn Z n m Z M Z Z m m j j 2 : : : > * UXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXj S M C S S S C F M m m m m M j C S C C C m m S m b m C S S G G G G G G G Y / / / / ` ` ` ` / ` / / ` / ` ( / ( G G S C C C C m k m k m b b m j m b b b b b 2 b b k b 2 b j j k m b k b 2 1 : : r b b D / ` k.@XwXbXNXFXwXk.) ~ D B Z A m h M M B B M M M B B B B B S K J F F J J J H J E H P H H H H Y Y _ _ _ H [ J VX,X_ H VXPX' ' ' ' ' ' ' ] _ _ J | @.4XwXVXVXVXPXCXwXM C Z A Z Z Z M C Z k : : 2 j : > UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUX2 S M J C C F S G F UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUX> S C S C S C C G S +X| X 1X] | 3X. X , 8XeXeX,X > O . n ..m G S F S S M n m M M m k M m C m M S S F G G G G / E / / / / / / E E / / G ( G G G G S C m m m m k m b b b b b b b j b b j b j j b : : : b k k l ~ ! k.k.k.x.k.k.j.( ( W K M M r 3XLXLXKXLXJ M C M S B C B K S J F K J F J J H H H H H H E H H E _ _ H / _ _ ] ] ] ] ] ' ' o.o.' H tXKX4X' .o.-.rXVXPXPXHXPXLXPXPXCXVXVX,Xk.| X.1X1XrXVXCX2Xj A C C k C A C Z n j 2 r 2 : : ; UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUX* S m G C F G G F m { ' . . k.4X' eXO . . _ 2X,XtX' | b . N eX. > k C S S m m m M m M M n C M C M F C S S G G G G G / G / E E / / G G E G G G S S S C C C m m m m b b b b b b j b b b j b j b n M A W W ~ ) j.! j.) ^ ~ W D B Z b h b m A n J > tXKXPXLXPXF B F B K F F J F J J H J H H H H H H H H P _ _ _ ] _ ] ] _ ] ] ] ] ] ' ' | | ' _ P X.tXLXeX] X.o.} ....| 4X4X k M k M n m C m m C M C M C M S G G G G G E G E G / G G G G G G G S G S S C C m m m b m b m b b b b b b b j j j n ~ W ! W ) W D D A A A n N m M M M M B C S B B B D b 8XPX4XtXPXB H J F P J J H H P P E H H / Y _ _ _ _ _ ] ] ] ] ] ] ` ] o.' | | | | { | _ ' X.eXVXHXCX-...1.k.,X1. . .k.| | ..1X1XrXrXVXvXbXyXz.w.0.a a i i a l Z Z k A C k S Z M n j 2 : : : j , UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXS m F F S 4 F C F Y ' , J * 3XM VXO j ; > C n M j M C C m M N M m C S G G G G G G E G G G G G G G G G G S S S Z Z k m b m b b b b m n r b j b j b j n E D A A n n M N m M B M B M B M B B B B B S B B H n eX' m _ 8X] J H H H H H Y E E E _ _ _ _ _ ] ] _ _ ] ] ] _ ] o.| | | ' ' ' ] _ _ | k.8XVXPXCXrX4XX...-.-.X.| X.,X1X0XVXrXCXvXbXjX/.B.c.t.c.p.p.9.7.p p p 7.y n k C A k C m n M b r j 2 : : j , UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXS C C C F Y F F Y N H 2 J ; b eXfXfXT & 3Xo.o.M ; > . * _ @.X.] eXH 4X> . j . X , j k M C b b b M m N M G G G G G G G G G G G G S G G S S S S S C C m m j m b b b b k b b b b j j j b j b D D h M B M M B B B A B B S K S K F F F J J J H J H rX_ _ B eXo.J E Y _ _ _ _ ] ] _ ] ] ] ] ] ' ' ' | o.| | X...o.] _ ` -.|.wXeXrXCXCXPXCXtXk.o.( o...OX1X2XtXVXVXHXkXhX[.T.P.J.J.u.t.9.y.i.b.i.y.e.8.p 7.7.0.R m M M Z m m Z Z Z j j 2 2 j : j & UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXS C C Y F J F G Y 2 j M . J X eXCXX.1X O yX1.F ,XN > o j | VX3X8XK . b X O - j m b b j m j C S G G S S G S G G G G G G S S G G S S S C m m m m b m b b b 2 b b j j j b j j b b W A Z B M B M F B S K F F F J J J J J H H H H H H ] eXH _ F rXX.J ] _ _ _ _ _ ] _ ' ] ' o.| X.' ' ' ' ' ' ' _ ` @.3XrXVXLXCXVXVXCXrX 2 & UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC m S G F Y G F Y N ; C . H * tXAX+ uXX X 4X..N 3X_ ; o 1X3XtX3XY ; ; * . . & ; k k k N m S G S G G G G G G G G S G S G S S S C C C m m m m b m b k m b b j b b j j j b j b W D F K F J F F J J J J H H J H E H H P H E P ] B X.rXK o.M rXCX' | ' ' o.| | o.| | X.{ ' ] ] X...@. j O UXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXm C C Y Y 4 Y Y / N > 3 X.[ o Y ; 8XAX 1X2 . O ,X,X2 8X@.[ . X k.....| Y wXeXLXK X @. j UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXj C C Y Y Y Y / Y / O -.uX5X, b S 3XPX T 2. . . ,X| , 3XCXAX8X o ,X3X/ H * K J ,X|.G * . X S G S S G G S G G S S G S G S G S S S C C m m m b b m m b b b b j b h j j j j b k ) ( J H E H _ _ _ _ _ ] ] ] ] ] ] ] ] ' | ] _ CXKX4X' | | | | X.X.tXtX,XeX3XVXVXHXCXVXeXOX{ _ | X.3X8XrXVXVXCXrXoXoXg.a.e.p.N.m.m.M.y.0.e.M.F.F.F.S.Q.%X;X4.:.;.5.-XF.F.F.F.H.F.C.u.e.y.y.e.7.i i i 7.7.7.7.i m Z M A Z Z Z Z Z Z j 2 2 r j > 2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX2 C m J Y Y Y Y Y ' o -.8X[ 3. ; G X j { j UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC m G Y Y Y Y Y ' . X.8X; 8X O F 1.AX, . uXo [ . o +XfXY 8XN . . X.8XJ ' O O > O J X.,X' ; o S S S G S S G S S G S G S S G S S C C Z m m m m m m b b b j j b j b b j j j j j r k.) _ ' ] ' ` ' ] ' ' ' | | | o.' _ ' 3XVXPXrX' | | .X.k.1.OX1.,X1.k.X. .-.1X8XrXVXVXCXvXoXjXW.B.i.t.t.t.t.q.7.p p 8.y.u.n.C.J.F.S.S.J.V.F.F.F.F.F.F.$X;X,.>.:.dX%XH.S.F.J.m.v.c.u.c.v.M.v.c.e.7.7.i i i p u M b m Z Z Z Z m Z k : : j : : : > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXS m Y Y Y Y Y ` ' O | 8X uX ; S | AXB 0X; . _ . X.CXj 5X<. . ' 8XJ / E ; O : . ; ] ,X-.Y + C G S S S S G G G S G S S S S S S Z C C m m m m k b b b j b j j b j j j j j j j r k.k.( ' ' ' o.o.X.X.| ' ` Y ] @..;.dX~.H.A.F.F.H.F.C.m.b.9.8.7.7.8.8.7.i g i 7.f C k m Z m Z Z Z Z n j 2 r : : 2 ; UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC m G G F Y _ T -., T CX uX+ ,XH M AXH 2.4 . Y X @.PXk T 1X . J 3X] F 1.eX5X* : X N { .;.dX=X^.S.F.F.F.m.c.e.7.9.u.m.V.m.y.8.p 7.7.8.f m Z n Z Z n Z k A b : r : : : r ; UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC m G Y Y Y T Y { 2 T AXo <.K 8X' + fX' 1.2. o H * ..KXB B 9X b 3X_ b ,XPXPXP ; , G G S S S S S S S G S S G S S S S C C M m C m m b m j b j b b b b b j j j j j j j k #XyX` ' _ ] | 4XrXrXHXPXVXVXeXX>.>.;.dX$XG.S.F.F.F.C.c.e.u.m.J.C.v.e.p p 7.7.8.e.c m n Z Z M Z Z C Z m : 2 2 2 : : , UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC N C Y S Y / Y -.N 1XAX# 3.1. eX-. + fX . V 8X o _ ; ..PX4 ; uX* B 3X_ , 4XCX2.8X, k G S S S G S S G S S S S S S S C C M C m m k m b m b b b b j r b j j : j j j j b #XyX2X3XVXNXAXCXVXtX1X|.' _ ] | ,X8X8XrXNXNXSXkXzX>XgX:.>.:.5.=XQ.F.G.K.G.H.S.V.N.V.F.F.F.H.m.u.9.7.7.7.7.p p 8.8.8.y.u.u.r.e.9.9.8.r.u.v.m.C.F.F.S.G.$X>X,.:.,.BX~.F.S.F.F.F.J.c.c.V.V.V.n.u.9.7.p a p 7.9.y m Z A k m n Z n m k : r 2 r > j , UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXm m S Y G H / _ -.J 1XfXj 3 2. 3X1. * fX| + uX . Y 2 | AXY X 3XN F 3X' , 3XfX1 fXV k S S S S G S S S S S S S S S S Z m m S m m m m 2 m b b b m b 2 j b 2 b j j j b r yXyXrXCXCXtX.:.BX=X!.S.F.F.V.r.u.n.C.C.C.m.c.8.p i 7.9.e.r.x m Z M Z n n Z Z Z k 2 r : j : j & UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXj M M Y Y Y _ _ _ 1.fX3X` O 0X -.2X * uX| X uX+ ; .;.5.;X:X$XF.F.F.H.m.m.m.c.r.e.u.m.V.F.V.c.u.e.8.9.7.7.7.7.8.8.y.M.V.F.V.m.u.u.u.M.J.F.F.D.^.:XBX:.>.>.BX=XQ.S.F.F.C.m.c.e.r.u.c.r.8.7.7.7.8.7.7.0.y k M Z Z m Z n Z C j j : : 2 > j X UXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX2 M C G H / Y _ / 1XVX .{ uX F 3X * uX{ * 1Xb . J tX; G fX' O T 0X . _ Y * * 8XtX 1X0X 2 G S S S S G S S S S S S S S S S S S m m m m 2 m b b b b b 2 m j j j j r j j b N z..XwXCXVXVXCXyXoXz.a.0.q.t.t.N.J.F.K.G.G.F.F.S.$X:X5.;.,.;.6.=XQ.D.F.F.F.F.F.F.F.V.C.H.F.J.M.c.y.9.8.7.p 7.9.y.u.c.c.c.M.v.c.u.u.N.M.V.M.C.F.F.F.S.^.=XdX;.>.,.>X%XF.S.F.S.C.c.e.y.M.n.M.m.m.v.e.p i p 7.0.A M n m k C k Z Z Z j : 2 r : : 2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX2 m C Y J Y Y G G { .,.>XQ.G.F.F.H.m.m.C.M.v.V.J.J.V.u.8.p p 7.7.0.Z m Z Z m n M n Z n j r r 2 r > 2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX, C m Y G S G Y G Y X { CX-.fX3 r tX + uX| K @.2. B tX: 1 tX' N X fXo . J F . VXCX j fX 2 S C S S S S C S S S S S C S m m S m S m m k m m b b b b k 2 k j j b b 2 j j 2 j x.l.s.p q.r.c.b.B.b.r.e.9.p.N.N.C.F.F.F.F.F.F.D.~.;X5.;.>.;.6.:X%XQ.F.F.F.F.F.F.C.M.u.u.v.m.J.C.m.M.c.y.e.8.8.9.y.u.y.r.c.V.H.F.F.M.m.M.m.F.F.F.F.S.G.%XBX:.:.,.-XQ.K.F.F.F.F.J.y.r.n.V.V.n.c.r.9.7.7.8.p.0.n A m k Z Z Z n Z n j j 2 : : > > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXS m G Y G Y Y G / O N eX2 uXrX| eXo O uX] H 3 1X , wXM N rX_ Y uX; . N B CXCX X ZX+ , G C S S S S S S S S G S C S S S m m S m m m m 2 b b b 2 m b b b j : 2 j j j b j x.x.w.0.9.e.r.u.N.V.J.F.F.J.m.m.C.F.J.F.F.F.F.~.=X;X,.:.>.:.6.:X&XS.F.F.F.F.C.M.c.c.V.H.F.H.V.M.c.y.r.y.v.V.J.V.m.v.u.c.m.C.C.J.n.e.9.r.M.V.F.F.F.D.G.%XdX;.:.4.-XQ.F.F.F.F.C.u.r.u.M.M.n.n.u.e.8.p 7.p 7.f Z Z n A Z A Z Z Z Z j : : : : r > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC C J J Y F G F / * 2 rX. 3 tX,XCX_ O fXY [ ; + 9X O 3X_ N rXK ` 1X4 . b N rXCXX 8X2 - G C S S S S S S S S S S S C S S S m m m m m b m m b b m 2 2 b b r b 2 j j r j j z.x.w.7.9.u.m.H.F.C.m.c.y.p.c.c.v.C.H.F.F.F.F.^.$X;X4.;.,.;.dX=XQ.S.F.F.F.F.C.J.C.C.m.M.M.v.m.m.m.u.y.c.c.u.y.y.c.c.c.v.M.n.M.v.v.c.M.c.y.y.m.H.F.S.G.%XdX;.:.4.;X%XQ.S.F.F.C.J.v.c.V.H.V.v.y.e.8.7.8.8.9.w.N k Z m Z Z Z M Z n j : : 2 r j ; UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXC m S J J F Y F Y , & eX, + 5X* rXwX1XfXV | -.. uX. . tX| K 1XN | 5X@. . X ,XN tXCX; 0XX. . - S C S S S S S S S S S S S S m S m m m m m k m m 2 k m 2 m 2 b j j : b r j j j j l.(.w.7.r.r.r.8.7.7.9.p.n.V.J.C.V.F.F.F.F.F.F.F.$X;X5.;.,.;.6.=X-XQ.F.F.F.F.F.J.M.v.m.V.F.F.F.C.c.y.y.y.y.r.r.9.8.7.7.9.r.y.v.v.m.n.N.v.m.J.F.F.D.H.Q.=XgX;.>.4.;X$XG.F.F.F.F.C.c.y.r.e.r.u.r.8.7.e.r.8.7.f m Z n Z n k Z k Z k r 2 : : : 2 , UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM m F F S G F G G ; X eXm uX, 8XT J AX3X3X3X 0X, eX} K j * UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXj m M F F G S G F k . 3X| 2.T k.T o uX; | VX+XfX5X ,X1X J 1X< 4X + fX. * rXo. tXtXN b fX & C m S C S S S S S S m S Z Z S C m m m m m m j m m k b b j j j j j j j 2 j j j j x.x.s.8.e.u.u.r.r.y.y.v.n.c.u.y.M.J.F.F.F.F.F.G.~.;XgX;.,.;.6.:X-X~.D.F.F.F.F.F.H.J.C.v.r.e.e.r.e.8.7.7.i p p 7.8.e.r.y.c.M.V.V.M.y.9.u.F.F.F.F.F.F.~.;X,.>.;.gX=XQ.F.F.F.F.C.m.n.v.v.M.M.c.r.8.8.8.8.9.y.f b k m n n Z Z n Z j : 2 : : : j & UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXk C m J J F J S S M X.,X T <.| | . uX1 ' 3X& 1.CX .3X3X J 1X. .;.6.:X-XD.D.F.F.m.n.m.V.v.u.r.u.c.c.u.e.8.7.p a i p 7.8.r.c.m.J.F.J.F.J.V.v.M.F.F.F.F.F.F.~.;X4.>.;.dX%XQ.S.F.C.V.V.m.n.n.v.v.c.c.c.u.e.7.7.i g r M Z Z Z Z n M Z n k : : j j > 2 X UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX2 M C F F J S C C N . o X. 1 5XG , O uX, T wXX + 9X_ rXVX& V 1X X S C S m S m S S S S m C C C C C m m m m b m b m j b b k 2 b b 2 j j r j j b r r x.l.w.i g p e.c.V.C.C.c.e.7.7.e.m.S.F.F.F.F.Q.~.%X;X4.:.>.;.dX:X&XS.F.F.F.F.J.m.u.r.u.c.n.M.v.n.M.c.e.9.8.7.8.7.8.e.M.C.C.F.V.v.y.y.y.y.v.C.F.F.F.F.$X;X4.:.;.dXQ.D.S.F.F.F.m.M.r.r.M.m.m.v.u.0.p i i 7.e.l Z Z M Z Z Z m Z m j 2 : : j > 2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX; M m F J J C C C m X ' . O uXG O uX, / 8X> . uXO ] eX4X8X1X ,X> 2.@. N ..,X ZXrXJ o rXT . X S C S S S S S S S S S S S C Z m S C m m m b b j b b b b b 2 b j b j j 2 b r r : x.x.w.p e.c.m.V.v.y.9.7.7.9.y.v.S.F.F.F.F.F.F.F.$X;X5.:.,.:.dX=XQ.S.F.F.F.F.V.u.9.8.e.c.m.J.Z.V.n.c.c.c.u.u.r.e.e.r.u.c.v.y.y.u.c.M.c.e.y.F.F.F.S.F.Q.-X4.:.;.dX%XH.S.F.F.F.m.c.c.y.e.7.a p p 7.7.r.b.M.m.x n Z n k Z Z k n m b r : : : : r UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM m F F F S S M M & H * uXX. O uX, H eXJ 0X> J * * 4XVX' .;.dX%XQ.S.F.F.F.H.F.C.v.u.v.m.V.m.V.C.J.J.H.C.N.r.8.8.7.8.e.y.v.m.V.H.C.V.M.c.c.C.F.F.S.G.Q.-X,.>.:.BX~.G.S.F.F.F.H.c.e.7.7.9.c.v.v.r.7.i p 8.0.Z n k n Z m M Z Z Z r 2 r j : : > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM m F G F F S m m * b ; 5X,X + uX+ M 3X' 1XV H , T tXY NX2X 3 5X J B . AX.:.BXQ.K.S.F.F.F.F.J.V.J.C.V.C.m.n.n.m.V.u.r.8.p p 7.7.7.q.y.n.C.J.F.J.V.v.r.y.v.J.F.F.F.Z.~.>X,.:.,.vXA.G.S.F.F.H.v.c.m.J.F.F.F.F.v.7.g p 7.7.p n n Z n Z n k D n m 2 : 2 : : > > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXM k G J F F J M M - ; b 3.8X + uX+ b 3X| 2.3.Z ; X.8X 1.wX4X.:.BX=X$XD.F.F.F.F.F.F.F.F.F.J.J.v.c.u.r.r.e.9.r.e.9.8.8.8.8.8.9.e.r.9.7.8.y.m.J.F.F.F.S.G.$X>X,.:.,.vXQ.K.S.F.F.F.F.J.v.m.C.J.M.u.9.9.9.7.p 7.p M Z k C Z M n k n Z j 2 r : r > ; UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXk m S G G G J S k ; , K [ fX + uX+ b 3X-. T 1X: ; 3.8X ' H ; .;.dX=X^.S.F.F.F.F.F.J.C.V.m.M.m.n.m.m.C.H.J.V.n.n.u.e.8.p g i p 7.p 8.r.c.v.c.M.F.F.F.S.F.~.BX>.:.>.BX~.G.S.F.F.F.m.8.8.c.c.c.y.y.r.9.8.8.e.y.w.n Z k m m k Z M n n > : : : : j ; UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe k S F C J D M M > O H 1XfX + uXo 2 1X-. 1 1XB ; @.5X S ,XF rX3XeX3X. , fX Y , N uX, . m C Z S Z C C S C C C Z C Z m C m m m m b m b j b b b k 2 j k j b 2 j j j 2 k > l.x.}.8.9.e.r.r.r.p.r.r.9.e.e.r.u.m.F.F.F.F.F.^.%X;X4.;.,.;.dX:X&XF.F.F.F.H.C.m.v.u.r.r.u.c.n.V.M.u.u.r.9.9.p i i 7.7.8.q.8.u.M.c.c.c.v.V.F.F.F.S.G.$XBX>.:.,.>X$XG.F.F.F.J.c.c.v.v.c.M.c.y.e.9.9.9.7.q.s m Z S k Z m M Z m k j r : : > 2 , UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw w k S S J F m Z > X ] 1XZX. + uX b ` | + uX] ; @.5X 2 3X4X 5XB F rX4XH : aX H ; ; 8XK . m C M Z Z C C S Z C S C C C S m Z m m m m m b m b j b b b b 2 j 2 k j 2 j 2 k 1 x.(.f p 7.8.e.r.e.e.r.r.u.y.r.u.C.F.F.F.F.F.F.Q.=X;X,.>.>.;.dX=XQ.S.F.F.F.F.F.F.V.m.V.C.C.C.M.r.7.a p i i i p 7.8.8.8.8.e.v.m.v.v.v.c.u.y.v.J.F.A.^.:X6.;.>.,.-X~.F.D.F.F.H.H.C.m.J.J.C.C.M.u.8.p i i g t Z Z S A Z A Z m M m 2 : 2 r > j * UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq w t t l A C M M r . ] T CX, X uX C X J X 8XX., @.2. , 8X3X 5X3.. | r 4XwXfX M M , 8X@. . j S S S C Z S C C Z Z Z C C m m m m m m m b b b b j j j b k 2 j 2 j j j j j j r l.x.w.p 7.7.p 7.9.p.N.M.v.v.y.n.S.F.F.F.F.F.D.^.=X;X:.;.:.;.dX=XQ.S.F.F.F.F.F.F.F.F.H.C.n.u.e.8.9.8.7.p 8.e.r.r.e.e.r.r.c.c.r.e.7.7.q.9.v.J.F.F.Q.&X=XdX:.:.4.-XS.F.F.F.F.J.C.m.m.m.V.m.c.9.7.p 7.7.p 7.f N n m A S Z Z n M k > > j > > j & UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX8 w e y y y y t k k K ] / fX3 X uX. S K , 8X1.* 1.5X X 3X3X [ <.. ' X _ CX,X,XH * <.5X . 2 S m C C C C S C C S C C C C C m m m m b m b b b b j b b j j j j j j j j 2 k 1 x.(.w.i g 7.r.v.J.J.J.V.C.V.C.F.J.F.F.F.F.F.F.H.$X;X4.:.>.;.BX%XP.S.F.F.F.F.F.m.v.r.e.8.9.e.r.r.9.7.8.9.r.r.r.u.y.e.e.8.7.p 7.r.v.m.H.F.F.F.F.S.S.H.$XdX:.:.4.-X~.F.F.F.F.F.V.u.u.c.r.9.8.e.r.8.7.7.8.8.f Z Z m k C Z Z m m k j j 2 j > j X UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX$ w e y t y y t e 8 . : 4X} fXT . uX C . C N eXaX& 1.2. o 3X3X N 1X. ] > tXN eXtXC ; P 3X . 2 S m C Z C C S Z S m C C C Z C m m m b m b b b b b j 2 b j j j j j j j j 2 m 1 l.(.w.7.u.m.V.J.C.H.F.F.F.F.F.F.C.H.F.F.F.F.F.Q.=X;X,.:.>.:.dX:X^.S.F.F.F.F.C.v.u.r.e.r.u.u.e.e.e.e.u.u.y.e.7.p i i p 7.r.m.F.F.H.m.c.m.H.F.F.F.S.G.=XdX;.>.4.;X~.F.S.F.F.C.c.u.u.u.y.c.M.u.8.p i i g p l h M S A Z C Z k k j > : r 2 > 2 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX% e w y y y e y e 6 @ N X,.;.>.:.BX=XG.S.F.F.F.F.F.H.J.J.F.J.J.C.F.J.C.n.r.p i g g p e.v.m.F.F.F.C.c.8.7.7.7.u.H.F.F.F.Q.=XgX;.;.5.-XQ.F.F.F.H.H.V.M.M.m.C.m.u.9.8.p a p 8.0.x C A Z S A k C Z n k j : 2 r : : UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe w l y y e t e w o b ,X-.8X0X: 8X b O , _ ,XCX> <.1. -.tX . fXJ ] V 8X , ' X ..eXAXS . > S M S C C m C C Z C C Z m Z C m m m m m 2 k b 2 m 2 j j j j j j j j j j j b 2 l.(.a.c.m.Z.V.M.M.c.u.y.c.M.J.F.F.F.F.F.F.F.S.Q.=X;X4.:.>.,.>XQ.F.F.F.F.F.F.F.C.J.F.F.F.H.n.c.r.7.p i i 8.r.u.u.M.m.V.C.m.c.c.r.e.8.8.v.F.F.F.F.D.Q.=X5.;.;.gX;XQ.F.F.F.F.C.y.p.u.p.y.r.e.e.7.7.a a s 7 l Z k Z C Z m k k m k : 2 : > j > UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXt q y y y t e t w @ r rX-.4XtXT 3X : * X _ _ AX3 2.1. X.rXO eX3XH O Y 8X O { ; j fXtX C Z m Z C Z C C Z C C C C C m C m m b m m m b b k m b b k 2 2 j j j j j 2 j 2 x.l.a.y.9.9.e.y.M.C.H.J.C.m.v.M.H.F.F.F.F.F.F.D.%X>X,.:.>.>.BX$XG.F.F.F.F.F.J.F.H.m.c.r.9.9.r.u.c.c.c.c.u.8.p 7.8.r.r.8.8.e.y.u.u.r.r.y.n.F.F.D.G.Q.-X5.;.;.5.-XD.F.S.F.C.y.8.8.q.p.c.M.v.9.p g 6 w v ( } ( ( S Z m Z S Z S 2 r > j > 2 = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe q y y t t y t e @ X o.' 4X9XfX1X ; ; . H N AXJ 2.1. H rX> 8XrXF < T eX o ' > O 1XY X,.:.>.>.BX=X^.F.F.F.F.F.J.u.7.a p r.m.F.F.F.C.M.u.e.7.p p p i p p e.M.C.J.V.m.c.r.y.V.F.F.F.F.Q.-X5.>.:.gX=XD.H.F.F.V.u.r.p.c.N.c.e.7.s d x Q j.} } } } } } ( D Z m m Z 2 : : r ; 8 % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe q y y l y y y w W ; . _ wX3.PX@. * > Y b HXX. 0X@. X ,XN T rXH O T 8X . ] , o 5X1.` { 3XAXNX] o . . , C m C C m C m C C Z C C m m m m m m m m b b b b b 2 j b j 2 j j j 2 j j 2 k 1 x.(.a.M.M.v.c.v.M.v.m.V.M.m.C.M.r.y.M.F.F.F.F.H.%X>X,.:.>.:.BX=X$XS.F.F.F.F.m.c.c.M.C.F.F.J.C.C.n.v.y.8.7.p a a i 7.r.u.v.v.n.m.m.v.n.F.F.F.F.F.F.$X;X4.>.:.dX$XF.F.F.F.F.V.c.t.7.s s c Q ! ) ) j.} j.} } } } } } } } ( G m k r > 8 $ $ % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw w l l y y w e w y X._ . K 3XM P ; X j ] , CX2. 8X4 . | J 2 VX_ O 1.5X ] X < X @.0Xo.< A tXHXrXJ + . , C C C M m C C C C C m C m Z m C m m m b b m b b m 2 b k b 2 j j j j j j 2 j 1 h.(.0.0.r.u.M.n.J.H.F.F.V.M.e.q.8.m.F.F.F.F.F.F.$X>X4.;.>.:.BX~.G.S.F.F.F.F.F.F.C.v.v.v.m.m.C.n.y.7.p 7.8.8.8.p p 7.8.e.c.m.C.m.y.u.m.J.H.F.F.F.F.Q.;X4.;.;.dX%XP.S.S.D.V.i.w.v d.h.h.h.j.j.) ) } ) } } } } ( } } } } } ( k q $ $ 5 $ = % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q e l y y y e e % . _ o.' _ . . m ' X & rX8X 8XB . _ F + ZXX.* <.5X ] * X , 2 tXJ o . B rXLXfX1.+ , S m C m Z C C Z m C Z m C m m m m m b m b m b b 2 k b b k 2 j 2 j j j j j k j }.x.}.7.7.8.r.c.c.M.c.c.u.r.y.v.V.F.F.F.F.F.S.Q.%X>X,.:.>.,.>X$XQ.D.F.F.F.F.F.V.m.n.M.M.c.c.e.8.8.e.u.u.r.9.7.7.8.9.u.V.V.c.r.8.e.r.u.C.F.F.F.F.F.D.-X4.>.>.dX$XG.S.G.Y.a.}.h.h.l.h.h.j.! j.j.j.) ) } ) } ) } } } G l q % $ q 8 $ $ $ = @ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq w t l l l y t t $ ; | ' X . K ] |.O eXCX 8X3 . J B X CX,X+ <.2. ] r . ; O fXX.. . 3 PXPXKXAXX.m C M M M M C C Z C C C Z C m m m b m m m 2 b k 2 b 2 k 2 b j j j j j j 2 j 2 h.x.f g g i 8.r.c.v.c.u.c.m.V.J.F.F.F.F.F.F.S.F.%X>X:.>.:.,.;X=X~.S.F.F.F.F.F.H.v.r.r.u.y.e.r.u.u.u.8.8.9.9.e.r.y.c.M.v.u.c.n.n.u.y.N.V.H.F.F.S.G.$X;X,.;.:.BXQ.I.!.T./.l.l.h.l.h.h.j.j.j.j.) ) ) ) } } ( ( D e q $ % $ q $ $ 5 $ $ $ = @ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq e e l z l l e e 8 . J ..b B J tXj .:.,.>X%XQ.F.F.F.F.F.V.u.u.m.C.n.c.n.v.c.r.u.c.u.e.q.7.9.r.y.y.u.M.u.e.p i 7.e.c.H.F.F.F.G.D.>X4.4.gXhX].'.'.(./.x.h.l.j.l.j.! j.j.) ) } ) ) ( l w 5 % $ $ 8 q $ $ $ $ $ $ 5 # $ o UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX% w e y x x l y t 9 * | | * C . j 3X' { fX4 uX; K ' rXrX+ 0X1. O | H S eXwX. o AX3 + | S m C m C m C C Z C C m Z C m m b m b m m b b b b j j j j j j j j j j j : b h ^ (.w.8.y.u.y.u.c.v.m.V.n.v.r.p p 8.V.F.F.F.Q.&X=XBX>.>.:.,.>X%XG.S.F.F.F.H.C.H.C.n.M.n.V.m.n.M.n.v.r.p i p p 7.r.y.p.r.9.p p 8.r.c.V.G.F.D.S.K.!.hXPXgXKXHXjX[.].'./.l.l.x.l.h.j.j.j.j.j.j.( D l q $ # # 8 8 8 = 5 = # = = = 5 $ $ $ = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe e R y l y y y w o O ,XX.C 2 ; 3Xo.1.8XtXfXo M ' O 0XfXo 1X@. . h.(.w.e.9.e.r.u.y.y.y.e.9.8.7.8.M.F.F.F.F.F.S.G.%X>X>.>.;.4.;X~.H.F.F.F.F.F.C.u.r.c.V.C.m.V.J.V.c.e.9.8.8.8.9.e.e.e.9.9.y.v.n.M.V.m.c.c.M.P.'.jXDXKXPXPXJXcXXX[.]._.).x.x.x.j.x.j.j.j.! R e 8 5 $ $ 8 8 8 8 5 = 8 = 8 8 8 = = $ $ $ $ = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe w y x l z l y t @ . ,X> ] |.* O 1X' 2XOXAXtX . b | < .;X=XQ.F.F.F.F.F.C.F.J.m.M.n.V.m.M.y.e.y.9.p g g i p p 8.r.y.y.r.r.r.r.i.f.(.[.jXzXDXKXLXPXJXSXlXXX[.{.).z.).l.j.j.j.~ l e 8 - $ 8 8 8 = $ = 5 8 8 8 = = = = = = = $ $ $ $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXt w l l l l l y y @ nX< n ..] { ' 3X,XV P : ] | o X.PX* 8XJ . o ' Y P VX . 5X9X . o m m C C C m m C C m m Z C C M m m k m m m b b j b j j j j j 2 r j j j j j b > h.x.s.@ g i 8.c.H.F.C.V.v.m.F.H.S.F.F.F.F.F.S.Q.=XBX:.>.:.:.>X:X~.F.F.F.F.F.F.C.r.7.9.u.c.m.n.n.u.8.i i p 8.e.y.r.e.8.q.7.0.a.}.(.).].XXjXmXSXHXLXLXLXFXzXhXjX_._._.z.j.h.Q v 0 = = = 8 8 8 8 5 $ 8 8 = 5 = = = 5 5 = = = = = $ = $ $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe w l l R l R R z l O . tX_ . o _ .:.,.;X=X~.S.F.F.F.F.J.v.u.c.n.m.V.M.u.r.9.0.e.y.r.e.e.7.p p f }.}.l.l.(._.[.jXhXxXSXDXGXJXGXSXzXlXjX*X_.z.j.c y 5 % $ # 0 8 = 5 = = 8 8 = $ 8 = 8 = 5 0 = = = = = = 5 $ 5 $ $ % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw w l z R R R ~ x 4X1. ; o nX|. . S |.' * > M 3X_ : CXT fX; N .. X AXO . rXP . m m Z C m m m C m C m m m m m m m b m b b b b b b j b 2 j j j j j j j j j b > h.z.}.M.V.n.c.u.u.c.v.v.n.V.C.n.V.F.F.F.F.F.S.G.%XBX>.>.:.,.;X%XQ.S.F.F.F.F.F.m.v.u.y.r.e.e.r.y.9.8.7.7.7.p s.w.}.}.l.l.l.(._._.[.jXhXzXmXFXFXGXSXFXvXlXjX_.j.Q t 5 % q q q $ 8 = 5 5 = = 5 = 8 = 8 = 8 = 5 = = = = = = # = 5 $ 5 $ = % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX0 8 y R z Q x ~ c wXpX. .N aX4X . , , J ,Xj > B 8Xo.: fX1X AXO b ,X aX; fX> . m m m m C m m Z C C m C Z m m m m m b n n b j b j j r b j j j j j j r b 1 j 1 h.(.a.e.e.r.y.v.M.C.J.H.V.v.y.m.F.F.F.F.F.F.F.Q.%XBX,.>.;.4.-X~.G.F.F.F.F.F.C.c.9.u.m.m.C.V.y.8.p i s s.w.}.h.}.h.l.l.x.z.oXoX[.jXkXkXcXcXmXFXFXSXvXoXk.c 9 % % 5 q q q $ 5 5 8 = 5 = 8 = = 5 $ = 8 8 = 5 = = = = 9 # = = = = $ $ 5 8 & UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX8 0 y R l l R ~ z ,X0X. iX7X PXqX , ; ; X.{ O . , M 3X| N rXeXX fX. ; 4Xo 8XV O AX+ m C C m m m C m C m M m M m m m m b n b b b n b j b j j j j j 2 j j j j j 1 1 }.f.w.7.7.8.e.c.V.J.V.c.r.e.r.v.C.F.F.F.F.F.D.^.%XBX>.>.;.4.;X~.F.F.F.F.F.V.m.n.C.C.m.n.u.0.7.p f }.}.h.h.}.}.x.l.x.x._._.oXjXjXlXzXvXmXcXmXvX.Xk.A 6 % % $ q 9 5 $ $ $ $ $ $ = 8 = 8 = 8 8 = 8 8 $ 8 = = 5 = = = = = = = # 5 $ = $ 8 @ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX= q y x R R x ~ h.k.fX1 ZX X aXaX < ; . K ,XM , M 3XX.F eXZX3 tX * .;.4.;X~.G.F.D.F.H.m.v.M.B.i.f.}.l.}.}.h.h.h.h.h.h.l.z.z._.z._.oXbXjXlXlX_.j.Q 5 % & 5 q 0 8 5 $ = = 8 5 8 8 $ = 8 8 8 = 8 = 5 = 8 5 = = 9 = = = = = = # = = = = = = 5 = $ # 5 o UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX% w e z x x R ~ ! W ZXVX_ ZXpX <.MXX o ; F |._ ; 4X2XY ,X5XPXT X ] eX, X.fX Y 8X . j M m m m C m M m C m m m m m m m m m b b b b b j b j j j j 2 j j j j j j b r h.l.}.8.r.c.m.C.J.J.V.M.N.u.u.v.J.F.F.F.F.F.S.^.=XBX;.>.:.4.;X%XF.D.D.Z.Y.B.f.a.l.}.l.l.}.l.l.}.h.h.l.l.z.z.l.z.z.).oX_._.z.! t $ % % 8 q q = $ $ $ 8 5 8 = 8 = $ 8 8 8 8 = 8 5 = = 8 5 5 = = = = 5 = 9 = 5 = # # = = 5 5 $ $ $ $ # 8 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe e l x R R R ! ) sX X| ZXZX; 7XLX, . . : O ' -.| o.' -.F L + o H tXN Y ZXX 1.8X . 2 M m C m m m C m m C m m M m m m j b m b b b b j j j j j j j j j j j j r b 1 R (.a.e.u.c.c.c.v.M.m.M.c.r.c.C.F.F.F.F.F.F.S.G.%XBX:.,.;.4.;XQ.G.L.U.T.W.f.f.f.l.f.f.}.l.}.}.h.h.h.l.h.x.l.z._.z.z.h.d 5 - % $ q w q 8 $ = = 8 8 = 8 = 5 5 = = 8 5 = = = 8 $ = 8 8 = = 8 5 = = = = = = = = = = 6 = 0 9 5 $ $ $ $ = = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe w x l R ~ ~ ! j.pXnXK 5XGXD 1.ZX; . F j N ..' ] m . / tX_ N fX; 9X0X . 2 m k m m m C m m m m m m m m m m m b b b b b j b j j j j j j j 2 j j j : b : ~ l.w.7.7.8.e.u.m.m.N.u.y.u.v.m.V.F.S.F.F.F.F.^.%XBX;.>.:.gX-X`.`.`.T.W.f.f.l.f.l.}.}.h.l.l.h.h.h.x.x.x.z.x.h.v d $ % - = 8 q 8 5 $ $ $ 8 8 8 8 8 = 8 = 8 = 8 5 8 $ 8 8 = 8 = = 8 5 = = = = 5 6 5 8 8 5 5 5 5 6 0 5 5 0 5 $ 5 = $ $ # UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe 0 x x R R R ~ ! 9XMXP 2.nX@.rXPXU < 3X . j O _ ,XB _ rX| > fX@. tX3. . > C M M m C m m m C C m C m m m b b b m m b b r j j j j j 2 j j j j j j b r 1 R }.w.p 8.r.b.n.c.c.M.c.M.c.u.v.V.F.F.F.F.F.S.A.~.dX,.5.AXLX*X[.'.'.'.'.(.(.l.l.l.l.g.l.l.h.h.l.l.x.h.h.l 5 # % $ 8 q q 5 $ $ $ = 8 8 = 8 $ 8 8 = 8 = 8 = 8 = = = 5 = $ 8 5 8 8 = = = 5 9 8 - < 8 8 8 5 5 5 5 = 6 9 9 9 5 = = $ $ = % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe q l x x R R R ! 5XZX7X5XnXh @.LX@.O AX^ M : X.o.o ] tX] - uX5X fXN > Z m m m m M m M m m m m b m k m b b m b b b b b 2 r j 2 b j r 2 j 2 b 2 b 1 c }.}.9.r.y.y.u.u.u.u.u.u.r.u.n.S.F.S.S.S.F.!.*XHXAXgXPXLXSXjX[.{.'.`.(.(.l.f.l.x.}.l.h.l.h.x.d.x t = # # = 8 8 8 = $ $ = 8 8 8 8 $ = 8 8 $ = 8 $ $ 8 = 5 = 5 = 8 8 8 5 8 = = 5 9 0 = - * 5 s a.0.# 9 5 9 9 5 9 9 9 0 0 5 5 = $ $ $ % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq e y x R R ! R R 7XsX4X@.ZX@.@.ZXrX .KX X N . X Y 8Xo.' F + KX4 * C j m m m m m m m m m m m m k m b 2 m k b k b b k j j j j j j 2 j j j 2 j j h R d p i i i p 0.}.l.x.(._.{.[.jXhXzXxXFXGXJXLXLXGXGXcXzXjXXXjX_.(.}.d 0 = = = 8 5 8 8 = = = 8 8 = 8 = 8 = 8 8 = = = 8 = = 8 = 8 = 5 8 = = 8 5 = = = = 6 8 8 * - w p i.Y.K.K.K.K.K.K.K.K.K.K.P.s.= 6 5 9 9 9 u q t 9 9 5 5 $ $ $ = @ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX# e e R R x x R ~ l @.7X@.sX7X1.aXiX[ fXnXo.6XPX` J GXP tXK ] . qX7X h O N 4XH * eX,X_ P 3 . & S m m m m m m m m m m b m m m m b b m b m b 2 b 2 j j 2 j j j j j j j j j : R c l u i f }.}.l.}.(._._.XXXX[.hXzXzXmXFXGXGXGXGXGXFXxXkXXX_.h.d = - # 8 8 0 8 8 8 = = 8 8 8 8 $ 8 = 5 = 8 = = 5 5 8 = 8 8 = = 5 8 = 8 8 = = = = 8 9 = - - 6 w.b.Y.K.L.L.K.K.K.K.K.K.K.K.K.L.P.s.= 9 8 6 9 u 9 e w 9 8 5 $ $ $ $ 6 o UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX& e w x x R x R ! x |.sX[ 6XsX@.uXnX[ uXnX^ @.AXqX/ CXMXh FX|. X. . nXtX : , * ' ,X..| o.H o & m N m m m m m m m m m m m m m m b b b b 2 j b j j 2 j j 2 j j : j j j j j : A h.R c }.l.l.l.x.(.{.oX[.XXhXhXzXcXmXmXDXGXGXGXGXSXlX.Xh.t # % # 8 0 0 = = 8 = 8 5 8 8 5 8 5 $ 8 8 = 5 = = 8 8 = 5 = 8 = 8 8 = 8 = = = = = 9 0 = * # 9 s.a.Y.K.K.K.K.P.K.K.L.K.K.K.K.K.K.K.K.K.s 0 u 9 9 q q 9 9 9 9 8 5 5 $ $ $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw w y R l R x ~ x XiX@.6X6X[ MXpX@.uXnXV 4 AX XK PXMX4X iXqX 8X1 o qXnX ; , b ..| H N + C m m b m m m m m m m m m b m m m 2 m b b b j b j k j j j j j j j j j j j 1 R ! Q l.h.l.x._.(._._.oXXXjXkXzXxXmXmXmXFXFXGXlX.XW 0 % < 5 8 0 8 5 = = 8 5 8 8 8 = = 8 = = 8 = 5 = 5 = 8 8 = = 8 = 8 = 8 5 = = = = 8 6 0 = * - u s.i.P.K.K.K.K.P.K.L.K.K.K.G.L.K.K.K.L.K.L.K.P.s 9 6 9 9 9 u t 9 9 w 8 $ 5 $ $ $ = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe 8 y x y x R ~ f qXiX2.sX6X1XaXpX|.1XaXL V ZXnXT aXZX|.. VX7X 0X3. X aXiX ; ; O _ R h.j.l.l.(.z.z.{.oXoXXXXXjXkXkXxXmXmXmXqXk.A < % % = 9 9 8 = $ $ = 8 5 8 = 8 5 = = 8 = 5 = = 8 = 5 = 5 = 8 5 8 = 5 8 8 = = = 5 9 8 = , - u s.b.P.K.K.K.K.K.K.K.K.K.K.L.K.L.{.G.K.K.K.L.K.K.K.P.7 q w w 9 6 w t 9 9 w 8 5 $ $ $ $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe w l x R x x R x ^ aX6X7X6X7XaX6XqX6XaX@.P VXtX1.0XnXtX 2 o.F . X C m m b m k m m m m m j m m b m b m k m b b j j j j j j j j j 2 j j : j j : A h.l.x.x.x.(._.oXoXXXoXhXkXkXkXlXoX^ t 5 @ # 0 e 9 5 $ $ = 8 8 8 = 8 5 = 5 = 8 = 8 = 8 = 5 8 = 8 = 8 = 8 = 8 5 = = = $ $ q 8 $ * = u s.b.P.K.L.L.P.K.K.K.K.L.L.K.K.K.L.S.jXJXK.I.K.K.K.K.K.K.P.7 t 7 w u 9 t 9 t w 9 8 $ = $ $ $ # UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe q h f x l R R R ^ <.pXpX6XaXpX7XqX5XMX|.I fXqX; 2.iX|.1 PXnX 5X0X o 4XiX O : * ,X| * X m m m m m m m m m 2 m m b m b m k b b j m j b j j j j j j j j : j j 2 j j j l h.x.l.)._._._._.*XjXjXXXoXk.c 5 @ % 5 9 0 0 5 # = = = 0 5 = 5 8 8 5 8 = 8 5 = 8 = 8 = 8 8 = = = 8 = 8 8 8 = = = 8 9 8 = - - 7 s.B.K.K.K.K.K.P.K.K.K.K.K.L.L.G.L.K.K.I.A.kXKXK.F.L.K.L.K.K.K.Y.u u 9 q q t 9 9 w 9 9 q $ $ $ $ = % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q d R l x f ~ f k.@.7XpXiXpX6X6X@.<.sX#.V aXsXO 2.iX|., uXAX4XaXuX. |.nXF M . X _ ,XJ o o m m m m m m m m m C m m m m m m b b b b j b 2 j j j 2 j j j j j j j j 2 j j A l.z.x.z._._.[.oXoXx.~ y % % % 8 0 0 0 5 = 5 5 = 0 8 = = 8 = 8 = = 5 = 5 8 = = 5 5 = 5 = = = 8 8 5 8 = = = 0 8 8 = , = u s.B.P.K.L.L.P.K.L.I.L.L.K.L.K.L.G.I.'.K.L.G.I.F.cXLXT.XXG.L.K.K.K.K.Y.6 u 9 u w u q t 9 9 q 5 $ $ 5 $ = % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX8 q y x x x R R R ^ @.sX6X@.pXpX6X7X2.6X XP sXMX1 2.ZXiX] fXZX^ pXZXo . o.iXB _ ' : ..X.; o k m m m m m m m m m m m m b m m b m b m j b j j j j j j j j j j j j j 2 j 2 A x.z._._._.x.h.R 0 % % $ 0 w 0 5 = # 5 8 8 8 5 0 = = = 5 = 8 8 8 = 5 = 5 = 8 8 8 8 5 = = 8 8 = = = 8 5 9 = - - 8 s 0.B.K.K.K.K.K.K.K.K.K.G.G.L.K.K.K.K.L.G.zXJXL.L.K.K.L.KXJXmXLXI.K.K.K.P.L.B.9 u w 6 u t 9 q q 9 t q $ $ 5 $ 8 & UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX5 w e x y l x R f k.^ uXqX@.6XnXsX6X#.6X6X#.fXsXh #.sX|.h fXGX@.3XfX..8X@ |.MXV _ aXb X Y ,XH X . q e j b m C 2 2 m m m m b k b b m b b j m k j j j 2 j j 2 j j 2 j j j r j 2 R z.x.h.c y 8 < # 5 0 q 8 $ $ = 5 0 8 5 5 8 = 5 5 8 5 8 5 8 = $ 8 8 = 8 = 8 8 = 8 = 8 8 5 = = 5 8 8 8 = * # s 0.B.L.L.K.K.K.K.K.K.K.K.L.G.'.{.F.L.K.K.L.K.I.LXLXI.K.L.G.`.DXmXJXxX'.F.L.K.K.K.i.0 u t t 9 t 9 6 9 w 9 $ $ $ $ 5 8 @ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX= e 0 x y x x x x ^ #.7X6X#.0XrX7XaX7X6X6X#.uXsX^ <.sX X|.fXZX4X3.fX,X3XC L MX X ,XJXh . ; X.@.2 . . q w w e m m m m m m m m m m m b m m j b j b j 2 j b j j j r 2 j j j b j 2 > y l 0 - % $ 8 0 0 8 = $ 8 8 8 0 = 5 8 5 5 0 5 5 8 8 = 8 5 = 8 = 5 8 = 8 5 = 8 8 8 $ = = = 8 0 8 # , = s 0.Y.L.K.K.K.P.P.K.K.K.K.L.L.K.I.A.xXxXA.U.G.K.L.F.'.hXkX`.F.I.G.`.kXI.GXkX{.F.I.K.I.L.i., u t t 9 q t w 9 t w 8 $ $ $ $ = @ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX& e 0 y t c d c d j.^ <.iX@.3.nX@.7XaXpX7X|.6XaX#.#.sX6Xb uXnX|.5X9XL | 0 0 = = = 8 0 q 8 8 $ = 5 8 8 8 8 8 5 5 5 5 8 = = 5 8 8 = = 8 = 8 = 8 = 5 = = 8 8 = = = 8 8 8 8 # * 5 s 0.Y.K.K.L.L.P.K.K.L.K.K.K.K.K.K.K.G.U.A.xXzXS.I.K.K.U.A.kXjXXX{.S.U.F.[.jXL.`.zXcXA.I.K.H.K.a.$ 7 q u 9 6 t t 6 t 6 $ 5 $ $ $ = o UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe 0 y d y y x u x.P 3.aX^ T nX7X#.sX6XpX7X7XsX[ #.7XiX@.ZXaXaX@.2.4XJ n eXMXrX 4XMX4X* - { +Xk w w e w w q q e k m m m m m m b m j b k b b 2 b b j j r r r r 8 8 $ 8 8 8 0 0 = 8 = 8 = 8 8 8 5 8 = 8 8 = = = 5 = 5 = 5 = = 5 8 5 8 8 5 = 8 5 8 = = = = 0 8 = - - 8 s i.Y.K.I.K.K.K.K.K.K.K.K.K.K.L.K.K.K.K.K.K.U.A.xXxXA.U.K.G.K.A.cXkXXX{.S.T.A.xXzXS.F.kXFXS.U.S.[.{.p.e 7 t t w t w 9 9 9 q $ $ $ 5 $ = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXu 5 y x d y x u j.^ [ MX^ 3.pX X1.pX|.pXpX4XpX|.1.6XiXV uXZX7X1XT @.P eXnXtXN _ nX@.. . . S m q e w q w e q q e q q e r k k b j k j j j r 2 e > q 8 q 8 5 5 8 8 8 8 8 = 8 = 8 8 8 8 8 8 8 5 8 8 8 8 8 0 5 5 = = 0 5 = 0 = 8 = = 8 8 8 5 = = = 8 9 5 8 - - 8 s i.Y.K.K.P.K.K.K.K.K.K.K.K.L.K.K.K.K.K.K.K.K.K.K.L.I.LXzXA.I.K.L.kXA.xXjX[.{.S.T.A.xXzXF.L.`.zXS.U.A.zXDX9.k d q u 9 6 9 t w t q $ $ $ $ $ = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw w y x t d c 7 j.|.j nX^ 2.aXeX@.6X@. XsXaXpX|.@.pXpXh <.ZXO.T T tXE n fXqXqX' rXiX X' 8 w w w e w w q w w q w w q w w q q q q q q q q $ q $ q q q $ $ q $ q $ q 8 8 8 8 8 8 8 8 8 8 5 5 = 8 8 = 8 = = 5 5 0 = = 8 = 8 8 5 8 = = = 8 6 8 = - * 9 f i.Y.K.L.K.K.P.K.K.K.K.K.K.L.K.L.K.K.K.L.K.K.K.K.L.K.I.S.jXKXjXA.I.G.I.LXL.hX`.XX'.A.K.A.kX[.F.K.T.zXI.I.G.cXKXd.e d 9 t 9 9 t 9 9 9 q 5 $ $ $ # = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe 9 t t y t x t ! ^ , qX@.@.pX@.<.fX@.O.6X6XpXpX<.<.sXI <.MX|.O.<.ZX^ rXaXqX 8XGX X: , o = e q w w w w e q e q e e e q q w q q q q q q q q q $ q - q q q $ q $ 8 8 8 8 8 8 8 8 8 8 8 8 = 8 5 8 8 8 8 = 0 5 = 5 5 = = 8 5 = = = 8 0 8 8 - - 5 s.i.Y.K.K.K.K.K.K.K.K.K.K.L.K.K.K.K.K.K.K.K.K.K.K.K.K.L.K.G.I.S.kXKXSXK.I.K.I.KXxXcXI.jX'.L.XXS.kX`.G.K.U.KXLXI.L.KXLXv e t t 9 w u 9 t w 9 5 $ $ $ $ $ # UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw q t y t y d d v W O qX|.T 9Xb #.uXiXP 6X7X|.6XaX7X6X@.<.sX4XI 1.iX@. ZXpX|.X 4XGXqX|.+ { @. # e w w w q e e q w q q q q e q e e q q q q q q $ $ q q q $ q $ q q $ q - 0 8 8 8 8 8 8 8 = 8 8 8 8 8 8 8 = 0 5 5 5 8 5 5 5 = = 8 8 5 = - # 6 f b.Y.K.K.K.L.K.K.K.K.K.L.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.L.K.K.L.G.I.A.hXcXJXU.F.K.T.KXDXJXL.kX`.K.JXI.SXU.L.I.`.KXKXU.kXKXLXz u t u u t t 9 9 9 t q $ $ $ $ $ % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX8 q t t d d y d t ) |.o. X4 0X [ aXnXI aX6X7X<.6XiX9X|. XsX7XI @.iX X ZXpXqX_ 1.AX#.4Xk.N | / X % e q q e e q q e w e w e e q w q q q q q q q - q q q $ $ q $ q $ $ q $ q 8 8 8 8 - q 8 8 8 8 8 5 = 5 = 8 = = = 5 8 8 = 8 8 9 0 = - - 6 s.i.Y.K.K.K.K.P.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.L.K.K.K.K.K.L.L.K.L.L.K.K.L.G.cX'.SX`.XXF.XXKXKXJXF.kX`.hXSXI.LXU.G.A.'.KXKXSXKXcXSX~ s y 7 t 9 w 9 u q q 8 $ $ $ $ = % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX= q t t d d t t d e H 3XqX: 9X> T uXiXL 5X6X7X@.uX@.aX7X2.sX6X@.@.6X X. 2.MX7X< ' ,X, , T MX|.P uXiXI 6XsX X<.aXpX|.6X[ 6XsX7X|.1XZX6XU n eXiX[ ^ NX3Xh . @ e q w q e q e q e w w e w e q q q q q q q q q 8 q q q $ 8 - q q 8 8 8 q - $ 8 8 8 8 = = = 8 9 9 = - = u s.B.K.L.K.K.P.K.K.L.K.K.K.L.K.L.L.K.K.K.K.K.K.U.A.zXDXS.I.K.K.L.G.`.[.FXSXU.K.K.K.L.G.'.LXKXJXmXxXA.`.T.KXkXA.xXXXJXI.kX[.jXcXSXJXSXKXzXDX`.K.L.G.K.K.L.KXkXS.a.= t q t 9 9 9 q w 6 q 5 $ $ $ $ = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw q t d d d d d 7 . b K o J ,X_ j 4 ZX|.V pX6X[ <.6X7X#.6XsXpX9X#.0X6X6X@.3XZX6X^ AXqX#. qXP ' CX8Xo X e e w w q e w w q q e q q e w q q q q q q q $ q $ $ q q 8 q q $ 8 8 8 8 q q 8 $ = 8 8 9 8 # - 8 s 0.B.K.K.L.P.K.P.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.L.G.I.A.kXJXG.L.K.K.L.G.xXKXmXkXJX`.K.K.L.F.zXJXKXDXcXcXA.SXhXLXxXA.L.A.SXJXJXFXkX'.L.LXLXKXcXjXG.K.K.L.K.L.L.KXjXF.a.% 7 6 9 9 9 w 9 w w q 5 $ $ 5 $ = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw q d d d d d d d . J M X N B fX3X] > tXiXh uXZX|.1 <.uXGX@.7X6XaXiX5X^ T #.^ T 6X5X X; @.qXiXO.% e 8 w w e q e e e q w w q q w q q q q q 8 - q 8 - q q 8 8 8 8 8 8 8 8 # d '.!.!.Y.Z.L.K.K.L.K.K.K.K.K.K.L.K.K.K.K.K.K.K.L.K.I.A.zXmXDX`.F.K.K.K.SXKXSXLXG.zXxXA.G.K.jXJXmXzX{.S.'.[.K.U.KXkXA.I.G.U.A.cXxXA.U.G.L.K.K.K.K.K.K.K.K.K.K.L.G.[.U.K.K.K.K.K.K.K.L.K.L.i.- u q 9 9 q q w 6 q 8 $ $ $ $ 5 # UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXl - q q v v v v v d.v v c d ) J ; . S ,XJ nXnXT KXrXK 1.9XC Y #.iXsXL <.7X2.5X7X2. X7XqX^ T aX9X XA 5 e 9 e q w q q e q q q w q q q q q q q 8 8 q $ q $ q 8 q 8 8 8 8 = 8 # d '.!.`.D.cXXXS.I.K.K.K.K.L.K.K.K.K.L.K.K.K.K.K.L.K.I.A.kX[.DXDXDXLX[.{.KXSXxX[.A.zXcXSXKXkXzX`.F.I.K.K.L.L.K.L.G.K.K.K.K.K.K.L.L.K.L.K.K.K.K.L.K.K.K.K.K.L.K.K.K.K.L.K.K.K.K.Z.Y.a.a.v w.v v d d d d d t 7 w 9 q $ $ $ $ 8 @ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX- e q q $ q $ t z v v v v c c c v d . O ; , ..o XiX+XAX7X 2.0XH ' 3.pXaXh <.6XuX<.iXuXqX7X6X^ 4 iX6X XR % e q q q q w q q e q w q e q q q q 8 q 8 8 q 8 q 8 8 8 8 8 8 8 8 - q $ 7 '.!.!.!.HXzXA.U.K.L.K.K.K.K.K.L.K.K.K.K.K.K.L.K.G.I.G.zX`.`.KXzXSXKXmXSXU.S.G.A.xXcXDXKXcXDX`.G.L.K.K.K.K.L.L.L.L.K.K.K.K.K.K.K.K.K.K.K.K.L.K.L.K.K.K.L.K.K.K.K.K.K.K.P.Z.B.a.d.s.d.s.v v s d v d d d d d d t 5 $ $ = $ 8 @ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXy q q q q q q d v x x v v c v x x 7 - ; _ o 4XeX> PXXhXJXKX[.G.L.K.K.K.K.K.K.L.K.K.K.L.I.I.G.S.L.[.LXDXxXKX{.F.DX[.kXF.K.I.S.zXDXhXLXFXkXK.K.K.K.K.K.K.L.K.L.K.K.K.L.K.L.K.K.K.K.K.K.K.L.K.K.K.K.K.K.K.K.L.P.B.a.a.a.a.g.d.w.w.d.d.s.v d v d d v 9 6 $ $ $ $ $ $ 8 % 8 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq $ q q q q q t v x x c x v c v x v % j + < 4XX * ; o.9XuX* j T 6XZX< T 6X7X9X7X6X6X#.9XiX2. X6X Xj.% e 8 q q e 0 w e q q w q w w q q - q 8 q 8 8 q 8 8 8 8 8 8 8 8 8 = 8 $ s ].*X].hX*XKXKXLX'.G.L.K.L.K.K.K.K.L.I.I.G.S.F.'.zXDXJXLXU.A.[.I.`.DXhXkXA.I.L.F.jXKXjX[.KXjXS.I.K.K.K.K.K.K.K.K.L.K.K.K.K.K.K.K.L.K.K.K.K.K.L.K.L.K.L.K.G.V.B.a.f.a.g.g.a.w.s.v w.v s v d v d d 9 $ $ 5 $ 5 $ $ $ $ = $ 8 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe q q q q q 8 t v c x x v v z v x v % * ,X. , @.gX3 .@.<.ZX^ V uXsX0XqX6X X#.2.qXfX7X7X@.o.# u 0 w q q e q 0 e q q w q q q 8 q q q q q q q 8 8 q 8 = 8 8 8 8 8 8 $ 7 ].].].*XjXLXLXKXkXA.I.K.L.K.K.L.L.K.F.S.T.jXSXSXzXJXcXKXI.I.G.F.`.SXcXzXA.I.L.S.jXKXI.F.KXkXA.I.G.L.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.L.K.L.K.K.K.K.K.D.n.f.f.a.g.g.a.g.p.d.w.d.v w.w.v f d d 9 $ $ $ 5 $ $ $ $ $ $ $ $ $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX% q q $ q $ q q e x v v x x z c v x v 9 ' * & P AX,X` K <.LXiX1 7XpX6X6X7X XU 2.iX0XpXaX@.) 5 9 9 q w q q w q e q w q w q q q q $ - q q $ 8 8 8 8 q q 8 8 8 8 8 8 $ 7 !.].].*X].SXxXSXxXA.I.G.L.I.L.F.G.I.[.cXDXzX{.F.G.JXJXSXG.K.L.K.U.JXJXzXA.I.L.G.{.JXI.U.KXXXS.I.K.K.K.K.K.K.K.K.K.L.L.K.K.K.K.L.K.K.K.K.P.P.K.K.Y.B.f.f.f.R.f.p.g.g.w.d.d.d.w.w.v f v t 9 $ $ 5 5 $ $ $ $ 8 $ $ $ $ $ $ % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX& q $ q q q q $ e v x x v c v x v x v 9 J . , T fX; 1X1.<.AX> 3 9XpXpX6X6XiX^ #.qX5X|.pXuX|.% e 8 e q w q q q q q q q q q q $ q q 8 q - q 8 8 - 8 = 8 8 8 8 8 8 8 5 t ].].*X].!.[.L.jXSXF.U.L.G.A.K.'.SXJXcX[.I.A.F.L.`.LXLXU.G.L.K.K.I.LXKXjXS.I.K.G.U.[.F.U.JXT.K.L.K.K.L.K.K.L.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.P.B.B.f.f.f.f.E.f.g.g.g.g.g.a.w.d.d.w.f u 6 $ $ $ $ 5 $ 5 5 $ = $ $ = $ $ $ $ # UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX@ q q q - q q q w x x x z z z x v x c t ; AXo G 3XuX8X, <.6XfXpXsXiX X^ 3.qX2.@.@.0XqX$ e q w q q q e w q w q q q q q q 8 q q = q 8 q 8 q = q 8 8 8 8 8 8 8 q t ].].].].!.F.G.{.KXjXA.G.`.hXSXcXKXzXA.G.K.I.I.G.T.KXLXI.K.K.K.K.I.JXKX[.G.L.K.K.K.G.K.U.JXU.K.K.K.K.K.K.K.K.K.L.K.K.K.K.K.L.K.K.L.K.L.Y.B.B.f.E.R.R.E.E.g.g.g.g.a.d.d.d.g.d.v t $ $ 5 5 5 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXe q q q $ q 8 w x z v z c x z z v l t o . O -.5X. : V 9XVXo , 2.6XiXsXaX Xh T iXuX@.I <.|.0 0 9 w q q q q q q q q 0 q q q 8 q q 8 q q $ q $ q $ 8 8 8 = 8 8 8 8 = u ].].].].!.I.U.A.SXzX[.FXSXzX'.F.LXkXF.U.K.K.K.L.xXKXLXI.K.L.K.L.G.DXzXG.L.K.L.K.K.L.K.L.I.K.L.K.K.K.K.K.K.K.K.K.L.K.K.K.P.K.K.P.K.Z.B.f.f.R.E.R.R.E.g./.g.g.w.g.a.d.d.w.v 6 $ $ $ $ 5 $ $ 5 $ $ $ 5 8 $ $ $ $ $ $ $ $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q q q $ q 8 w x z x v z v c z v x t o . O -.o M ' 3.fXH o.5X5XiX5XeXiXh 4 iX6X X[ 2.|.0 8 w q q r q q w e q q w q q q q q 8 q 8 q $ $ q - q 8 = 8 8 0 8 = q = t ].].].*X!.A.L.'.DXLXzX[.I.S.G.I.KXxXS.L.G.L.K.I.LXKXLXI.K.K.K.L.G.'.I.G.L.K.K.K.K.K.K.L.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.P.Y.a.f.f.R.R.f.R.f.E.f./.g.g.g.a.d.d.d.v 7 # # 5 8 $ $ $ $ $ $ $ 5 $ $ $ $ $ $ $ $ $ $ $ % UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXw - $ q 8 q 8 q z c z x z z z z x x t @ ` H 1 & 0X8X. 3X0XfXiX2.7X7XU 4 sX7X|.I 1. Xy 5 e q q q q w q e q w w q q q 8 8 q $ $ q q q 8 q 8 = 0 8 8 8 8 8 8 $ t ].*X*X].].hXDXcXzXcXA.[.JXI.I.I.KXSXL.I.L.U.S.[.hXkXJXG.I.K.K.K.K.G.K.L.L.K.K.K.K.L.K.K.L.K.K.K.K.L.L.K.K.K.K.L.K.K.L.K.V.B.f.a.R.R.R.a.E.R.g.g.g.E.E.g.a.g.g.d.t 5 % $ 8 $ $ $ $ $ $ $ 5 8 = = $ = $ $ $ $ $ $ $ $ 8 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q 8 q 8 q q q x z x z c c c z x v x @ * _ . ; k.<.0X , aXZXiX@.8XiXh 1 uXpX@.L @.qXA # w q q e w q q q q 0 q w q q 8 q = q q q $ 8 8 8 8 8 8 8 8 = = 8 8 $ 7 ].*X*X].jXzX'.S.K.DXhXKXLXI.G.U.JXLXK.F.F.L.A.cXhXjXSXA.I.G.L.K.K.L.K.K.K.K.K.L.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.L.Z.Y.R.f.f.R.E.R.R.E.W.f.l.g././.g.g.E.d.v t - % $ 8 5 $ $ $ $ 5 5 5 $ $ $ $ = $ $ $ $ $ $ $ $ $ $ 8 UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q 8 8 8 q $ q x z v c z x z z x x d % b o. + K b S o 5XAXGX3.aXMX1 + 0X8X XV 3. Xh 5 t q q w q q w q w q q q q q 8 8 q 8 8 q = 8 q 8 8 0 = = = 5 8 = 8 # t ].*X].].!.A.G.K.T.JXJXKXLX`.S.'.zXDXFXU.[.XXA.DXI.hXxXA.I.K.L.K.K.K.K.K.L.K.K.K.K.L.L.K.K.K.K.K.K.K.K.K.K.K.L.Y.B.f.R.R.f.B.R.E.R.f.f./././.E.g.g.g.d.f 9 $ % 5 8 $ = 5 = $ $ $ = $ $ $ = $ $ $ $ $ $ $ # = $ $ $ = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q q 8 q q q q l z x z z c z z z x c 6 . ; @.o . ' 4XX K ZXfX3XZXMX; O uX3X Xh #. XR 8 0 q q q q w q 0 q 0 w q q q q q q q q = q 8 8 $ 8 8 8 0 = 8 8 8 8 $ 9 ].].*X].].L.I.K.I.LXKXJXSXKXS.jXkXL.KXhXJXxXF.LXI.L.L.K.L.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.L.K.L.K.K.K.P.Y.B.f.f.W.E.R.f.R.R.R.E.R.g.g.g.g.g.g.d.d 6 $ $ 5 8 $ $ 5 $ $ $ 5 5 $ $ $ $ $ $ $ $ $ $ $ $ $ = $ $ $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq - q $ q q 8 q x Q z z z c z z c x x 7 . ` . o F M . J KX3 T & 4XGX1 9XZX X< P 7XA 8 w 0 q q w q q q q q q q q q 8 q 8 8 8 = q q $ q q 8 8 = 8 5 8 = 8 8 6 B.].].!.L.L.K.I.A.hXzXXXI.jXSXSXU.K.T.LXxXzX`.JXI.K.L.K.K.K.L.K.K.K.L.K.K.K.K.L.K.K.K.K.L.K.K.K.Y.B.f.f.f.R.R.E.W.R.R.E.E.E.E.g.l./.g.g.d.w % - = 8 $ $ $ $ $ 5 8 $ $ $ $ 5 $ $ $ $ $ $ $ $ $ $ $ $ = $ # # = UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq 8 8 q 8 q q q l z z z z Q z Q c z z q ; . . j 1.eX 2.AX X< I 7Xe e q q q q q q q q 0 q q q q q 8 q 8 q 8 q $ q q 8 $ 8 8 8 8 8 8 8 5 8 5 R.!.!.!.L.K.K.K.K.L.K.F.S.hXKX[.S.L.K.KXxXSX'.xXK.L.K.K.K.K.K.L.K.K.K.K.L.K.K.K.P.L.K.K.K.P.Y.a.f.f.R.E.f.W.).R.R.E.E.R./.g.l./.f.d.f # # $ 8 5 $ $ $ $ $ $ $ 5 $ $ 5 = $ $ $ $ $ $ $ $ $ $ = # $ $ = % $ $ UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUX", "UXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXq q = q q $ q $ y z z z z Q c z z x z d o j . #else #include #endif #endif #endif const char filePathSeparator = #ifdef _WIN32 '\\'; #else '/'; #endif #define BUF_SIZE (16384*6) #define DEFAULT_SAMPLE_RATE 2500000 // #define DEFAULT_FFT_SIZE 2048 #define DEFAULT_DMOD_FFT_SIZE (DEFAULT_FFT_SIZE / 2) #define DEFAULT_SCOPE_FFT_SIZE (DEFAULT_FFT_SIZE / 2) //Both must be a power of 2 to prevent terrible OpenGL performance. //TODO: Make the waterfall resolutions an option. #define DEFAULT_MAIN_WATERFALL_LINES_NB 512 // 1024 #define DEFAULT_DEMOD_WATERFALL_LINES_NB 256 #define DEFAULT_DEMOD_TYPE "FM" #define DEFAULT_DEMOD_BW 200000 #define DEFAULT_WATERFALL_LPS 30 //Dmod waterfall lines per second is adjusted //so that the whole demod waterfall show DEMOD_WATERFALL_DURATION_IN_SECONDS //seconds. #define DEMOD_WATERFALL_DURATION_IN_SECONDS 4.0 #define CHANNELIZER_RATE_MAX 500000 #define MANUAL_SAMPLE_RATE_MIN 2000000 // 2MHz #define MANUAL_SAMPLE_RATE_MAX 200000000 // 200MHz (We are 2017+ after all) //Represents the amount of time to process in the FFT distributor. #define FFT_DISTRIBUTOR_BUFFER_IN_SECONDS 0.250 //The maximum number of listed sample rates for a device, to be able to handle //devices returning an insane amount because they have quasi-continuous ranges (UHD...) #define DEVICE_SAMPLE_RATES_MAX_NB 25CubicSDR-0.2.3/src/DemodLabelDialog.cpp000066400000000000000000000055451322677621400175400ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "DemodLabelDialog.h" #include "wx/clipbrd.h" #include #include "CubicSDR.h" wxBEGIN_EVENT_TABLE(DemodLabelDialog, wxDialog) EVT_CHAR_HOOK(DemodLabelDialog::OnChar) EVT_SHOW(DemodLabelDialog::OnShow) wxEND_EVENT_TABLE() DemodLabelDialog::DemodLabelDialog(wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstancePtr demod, const wxPoint & position, const wxSize & size, long style) : wxDialog(parent, id, title, position, size, style) { wxString labelStr; //by construction, is allways != nullptr activeDemod = demod; labelStr = activeDemod->getDemodulatorUserLabel(); if (labelStr.empty()) { //propose a default value... labelStr = activeDemod->getDemodulatorType(); } dialogText = new wxTextCtrl(this, wxID_LABEL_INPUT, labelStr, wxPoint(6, 1), wxSize(size.GetWidth() - 20, size.GetHeight() - 70), wxTE_PROCESS_ENTER); dialogText->SetFont(wxFont(15, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD)); Centre(); dialogText->SetValue(labelStr); dialogText->SetSelection(-1, -1); } void DemodLabelDialog::OnChar(wxKeyEvent& event) { int c = event.GetKeyCode(); //we support 16 bit strings for user labels internally. wxString strValue = dialogText->GetValue(); switch (c) { case WXK_RETURN: case WXK_NUMPAD_ENTER: //No need to display the demodulator type twice if the user do not change the default value... //when comparing getDemodulatorType() std::string, take care of "upgrading" it to wxString which will //try to its best... if (strValue != wxString(activeDemod->getDemodulatorType())) { activeDemod->setDemodulatorUserLabel(strValue.ToStdWstring()); } else { activeDemod->setDemodulatorUserLabel(L""); } wxGetApp().getBookmarkMgr().updateActiveList(); Close(); break; case WXK_ESCAPE: Close(); break; } if (event.ControlDown() && c == 'V') { // Alter clipboard contents to remove unwanted chars wxTheClipboard->Open(); wxTextDataObject data; wxTheClipboard->GetData(data); std::wstring clipText = data.GetText().ToStdWstring(); wxTheClipboard->SetData(new wxTextDataObject(clipText)); wxTheClipboard->Close(); event.Skip(); } else if (c == WXK_RIGHT || c == WXK_LEFT || event.ControlDown()) { event.Skip(); } else { #ifdef __linux__ dialogText->OnChar(event); event.Skip(); #else event.DoAllowNextEvent(); #endif } } void DemodLabelDialog::OnShow(wxShowEvent &event) { dialogText->SetFocus(); dialogText->SetSelection(-1, -1); event.Skip(); } CubicSDR-0.2.3/src/DemodLabelDialog.h000066400000000000000000000015141322677621400171750ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/dialog.h" #include "wx/textctrl.h" #include "wx/string.h" #include "wx/button.h" #include "DemodulatorInstance.h" #define wxID_LABEL_INPUT 3002 class DemodLabelDialog : public wxDialog { public: DemodLabelDialog( wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstancePtr demod = nullptr, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE); wxTextCtrl * dialogText; private: DemodulatorInstancePtr activeDemod = nullptr; void OnEnter ( wxCommandEvent &event ); void OnChar ( wxKeyEvent &event ); void OnShow(wxShowEvent &event); DECLARE_EVENT_TABLE() }; CubicSDR-0.2.3/src/FrequencyDialog.cpp000066400000000000000000000206701322677621400175050ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "FrequencyDialog.h" #include "wx/clipbrd.h" #include #include "CubicSDR.h" wxBEGIN_EVENT_TABLE(FrequencyDialog, wxDialog) EVT_CHAR_HOOK(FrequencyDialog::OnChar) EVT_SHOW(FrequencyDialog::OnShow) wxEND_EVENT_TABLE() FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstancePtr demod, const wxPoint & position, const wxSize & size, long style, FrequencyDialogTarget targetMode, wxString initString) : wxDialog(parent, id, title, position, size, style) { wxString freqStr; activeDemod = demod; this->targetMode = targetMode; this->initialString = initString; if (targetMode == FDIALOG_TARGET_DEFAULT) { if (activeDemod) { freqStr = frequencyToStr(activeDemod->getFrequency()); } else { freqStr = frequencyToStr(wxGetApp().getFrequency()); } } if (targetMode == FDIALOG_TARGET_BANDWIDTH) { std::string lastDemodType = activeDemod?activeDemod->getDemodulatorType():wxGetApp().getDemodMgr().getLastDemodulatorType(); if (lastDemodType == "USB" || lastDemodType == "LSB") { freqStr = frequencyToStr(wxGetApp().getDemodMgr().getLastBandwidth()/2); } else { freqStr = frequencyToStr(wxGetApp().getDemodMgr().getLastBandwidth()); } } if (targetMode == FDIALOG_TARGET_WATERFALL_LPS) { freqStr = std::to_string(wxGetApp().getAppFrame()->getWaterfallDataThread()->getLinesPerSecond()); } if (targetMode == FDIALOG_TARGET_SPECTRUM_AVG) { freqStr = std::to_string(wxGetApp().getSpectrumProcessor()->getFFTAverageRate()); } if (targetMode == FDIALOG_TARGET_GAIN) { if (wxGetApp().getActiveGainEntry() != "") { freqStr = std::to_string((int)wxGetApp().getGain(wxGetApp().getActiveGainEntry())); } } dialogText = new wxTextCtrl(this, wxID_FREQ_INPUT, freqStr, wxPoint(6, 1), wxSize(size.GetWidth() - 20, size.GetHeight() - 70), wxTE_PROCESS_ENTER); dialogText->SetFont(wxFont(15, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD)); Centre(); if (initString != "" && initString.length() == 1) { dialogText->SetValue(initString); dialogText->SetSelection(2, 2); dialogText->SetFocus(); } else { if (initString != "") { dialogText->SetValue(initString); } dialogText->SetSelection(-1, -1); } } void FrequencyDialog::OnChar(wxKeyEvent& event) { int c = event.GetKeyCode(); long long freq, freq2, freq_ctr, range_bw; double dblval; std::string lastDemodType = activeDemod?activeDemod->getDemodulatorType():wxGetApp().getDemodMgr().getLastDemodulatorType(); std::string strValue = dialogText->GetValue().ToStdString(); bool ranged = false; std::string strValue2; size_t range_pos; switch (c) { case WXK_RETURN: case WXK_NUMPAD_ENTER: // Do Stuff ranged = false; if ((range_pos = strValue.find_first_of("-")) > 0) { strValue2 = strValue.substr(range_pos+1); strValue = strValue.substr(0,range_pos); if (targetMode == FDIALOG_TARGET_DEFAULT && !activeDemod && strValue.length() && strValue2.length()) { ranged = true; } } if (targetMode == FDIALOG_TARGET_DEFAULT) { if (ranged) { freq = strToFrequency(strValue); freq2 = strToFrequency(strValue2); } else { freq = strToFrequency(strValue); } if (activeDemod) { activeDemod->setTracking(true); activeDemod->setFollow(true); activeDemod->setFrequency(freq); activeDemod->updateLabel(freq); } else { if (ranged && (freq || freq2)) { if (freq > freq2) { std::swap(freq,freq2); } range_bw = (freq2-freq); freq_ctr = freq + (range_bw/2); if (range_bw > wxGetApp().getSampleRate()) { range_bw = wxGetApp().getSampleRate(); } if (range_bw < 30000) { range_bw = 30000; } if (freq == freq2) { wxGetApp().setFrequency(freq_ctr); wxGetApp().getAppFrame()->setViewState(); } else { if (wxGetApp().getSampleRate()/4 > range_bw) { wxGetApp().setFrequency(freq_ctr + wxGetApp().getSampleRate()/4); } else { wxGetApp().setFrequency(freq_ctr); } wxGetApp().getAppFrame()->setViewState(freq_ctr, range_bw); } } else { wxGetApp().setFrequency(freq); } } } if (targetMode == FDIALOG_TARGET_BANDWIDTH) { freq = strToFrequency(strValue); if (lastDemodType == "USB" || lastDemodType == "LSB") { freq *= 2; } if (activeDemod) { activeDemod->setBandwidth(freq); } else { wxGetApp().getDemodMgr().setLastBandwidth(freq); } } if (targetMode == FDIALOG_TARGET_WATERFALL_LPS) { try { freq = std::stoi(strValue); } catch (exception e) { Close(); break; } if (freq > 1024) { freq = 1024; } if (freq < 1) { freq = 1; } wxGetApp().getAppFrame()->setWaterfallLinesPerSecond(freq); } if (targetMode == FDIALOG_TARGET_SPECTRUM_AVG) { try { dblval = std::stod(strValue); } catch (exception e) { Close(); break; } if (dblval > 0.99) { dblval = 0.99; } if (dblval < 0.1) { dblval = 0.1; } wxGetApp().getAppFrame()->setSpectrumAvgSpeed(dblval); } if (targetMode == FDIALOG_TARGET_GAIN) { try { freq = std::stoi(strValue); } catch (exception e) { break; } SDRDeviceInfo *devInfo = wxGetApp().getDevice(); std::string gainName = wxGetApp().getActiveGainEntry(); if (gainName == "") { break; } SDRRangeMap gains = devInfo->getGains(SOAPY_SDR_RX, 0); if (freq > gains[gainName].maximum()) { freq = gains[gainName].maximum(); } if (freq < gains[gainName].minimum()) { freq = gains[gainName].minimum(); } wxGetApp().setGain(gainName, freq); wxGetApp().getAppFrame()->refreshGainUI(); } Close(); break; case WXK_ESCAPE: Close(); break; } std::string allowed("0123456789.MKGHZmkghz"); // Support '-' for range if (targetMode == FDIALOG_TARGET_DEFAULT && !activeDemod && strValue.length() > 0) { allowed.append("-"); } if (allowed.find_first_of(c) != std::string::npos || c == WXK_DELETE || c == WXK_BACK || c == WXK_NUMPAD_DECIMAL || (c >= WXK_NUMPAD0 && c <= WXK_NUMPAD9)) { #ifdef __linux__ dialogText->OnChar(event); event.Skip(); #else event.DoAllowNextEvent(); #endif } else if (event.ControlDown() && c == 'V') { // Alter clipboard contents to remove unwanted chars wxTheClipboard->Open(); wxTextDataObject data; wxTheClipboard->GetData(data); std::string clipText = data.GetText().ToStdString(); std::string pasteText = filterChars(clipText, std::string(allowed)); wxTheClipboard->SetData(new wxTextDataObject(pasteText)); wxTheClipboard->Close(); event.Skip(); } else if (c == WXK_RIGHT || c == WXK_LEFT || event.ControlDown()) { event.Skip(); } } void FrequencyDialog::OnShow(wxShowEvent &event) { if (initialString.length() == 1) { dialogText->SetFocus(); dialogText->SetSelection(2, 2); } event.Skip(); } CubicSDR-0.2.3/src/FrequencyDialog.h000066400000000000000000000024461322677621400171530ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/dialog.h" #include "wx/textctrl.h" #include "wx/string.h" #include "wx/button.h" #include "DemodulatorInstance.h" #define wxID_FREQ_INPUT 3001 class FrequencyDialog: public wxDialog { public: typedef enum FrequencyDialogTarget { FDIALOG_TARGET_DEFAULT, FDIALOG_TARGET_CENTERFREQ, FDIALOG_TARGET_FREQ, FDIALOG_TARGET_BANDWIDTH, FDIALOG_TARGET_WATERFALL_LPS, FDIALOG_TARGET_SPECTRUM_AVG, FDIALOG_TARGET_GAIN } FrequencyDialogTarget; FrequencyDialog ( wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstancePtr demod = nullptr, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE, FrequencyDialogTarget targetMode = FDIALOG_TARGET_DEFAULT, wxString initString = ""); wxTextCtrl * dialogText; private: DemodulatorInstancePtr activeDemod; void OnEnter ( wxCommandEvent &event ); void OnChar ( wxKeyEvent &event ); void OnShow(wxShowEvent &event); FrequencyDialogTarget targetMode; std::string initialString; DECLARE_EVENT_TABLE() }; CubicSDR-0.2.3/src/IOThread.cpp000066400000000000000000000056571322677621400160730ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "IOThread.h" #include #include #define SPIN_WAIT_SLEEP_MS 5 IOThread::IOThread() { terminated.store(false); stopping.store(false); } IOThread::~IOThread() { terminated.store(true); stopping.store(true); } #ifdef __APPLE__ void *IOThread::threadMain() { terminated.store(false); stopping.store(false); try { run(); } catch (...) { terminated.store(true); stopping.store(true); throw; } terminated.store(true); stopping.store(true); return this; }; void *IOThread::pthread_helper(void *context) { return ((IOThread *) context)->threadMain(); }; #else void IOThread::threadMain() { terminated.store(false); stopping.store(false); try { run(); } catch (...) { terminated.store(true); stopping.store(true); throw; } terminated.store(true); stopping.store(true); }; #endif void IOThread::setup() { //redefined in subclasses }; void IOThread::run() { //redefined in subclasses }; void IOThread::terminate() { stopping.store(true); }; void IOThread::onBindOutput(std::string /* name */, ThreadQueueBasePtr /* threadQueue */) { }; void IOThread::onBindInput(std::string /* name */, ThreadQueueBasePtr /* threadQueue */) { }; void IOThread::setInputQueue(std::string qname, ThreadQueueBasePtr threadQueue) { std::lock_guard < std::mutex > lock(m_queue_bindings_mutex); input_queues[qname] = threadQueue; this->onBindInput(qname, threadQueue); }; ThreadQueueBasePtr IOThread::getInputQueue(std::string qname) { std::lock_guard < std::mutex > lock(m_queue_bindings_mutex); return input_queues[qname]; }; void IOThread::setOutputQueue(std::string qname, ThreadQueueBasePtr threadQueue) { std::lock_guard < std::mutex > lock(m_queue_bindings_mutex); output_queues[qname] = threadQueue; this->onBindOutput(qname, threadQueue); }; ThreadQueueBasePtr IOThread::getOutputQueue(std::string qname) { std::lock_guard < std::mutex > lock(m_queue_bindings_mutex); return output_queues[qname]; }; bool IOThread::isTerminated(int waitMs) { if (terminated.load()) { return true; } else if (waitMs == 0) { return false; } //this is a stupid busy plus sleep loop int nbCyclesToWait = 0; if (waitMs < 0) { nbCyclesToWait = std::numeric_limits::max(); } else { nbCyclesToWait = (waitMs / SPIN_WAIT_SLEEP_MS) + 1; } for ( int i = 0; i < nbCyclesToWait; i++) { std::this_thread::sleep_for(std::chrono::milliseconds(SPIN_WAIT_SLEEP_MS)); if (terminated.load()) { return true; } } std::cout << "ERROR: thread '" << typeid(*this).name() << "' has not terminated in time ! (> " << waitMs << " ms)" << std::endl << std::flush; return terminated.load(); } CubicSDR-0.2.3/src/IOThread.h000066400000000000000000000142341322677621400155270ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include #include #include #include #include #include #include "ThreadBlockingQueue.h" #include "Timer.h" struct map_string_less : public std::binary_function { bool operator()(const std::string& a,const std::string& b) const { return a.compare(b) < 0; } }; template class ReBufferAge { public: ReBufferAge(PtrType p, int a) { ptr = p; age = a; } PtrType ptr; int age; virtual ~ReBufferAge() {}; }; #define REBUFFER_GC_LIMIT 100 #define REBUFFER_WARNING_THRESHOLD 150 template class ReBuffer { typedef typename std::shared_ptr ReBufferPtr; public: //Virtual destructor to assure correct freeing of all descendants. virtual ~ReBuffer() { //nothing } //constructor ReBuffer(std::string bufferId) : bufferId(bufferId) { //nothing } /// Return a new ReBuffer_ptr usable by the application. ReBufferPtr getBuffer() { std::lock_guard < std::mutex > lock(m_mutex); // iterate the ReBufferAge list: if the std::shared_ptr count == 1, it means //it is only referenced in outputBuffers itself, so available for re-use. //else if the std::shared_ptr count <= 1, make it age. //else the ReBufferPtr is in use, don't use it. ReBufferPtr buf = nullptr; outputBuffersI it = outputBuffers.begin(); while (it != outputBuffers.end()) { //careful here: take care of reading the use_count directly //through the iterator, else it's value is wrong if a temp variable //is used. long use = it->ptr.use_count(); //1. If we encounter a ReBufferPtr with a use count of 0, this //is a bug since it is supposed to be at least 1, because it is referenced here. //in this case, purge it from here and trace. if (use == 0) { std::cout << "Warning: in ReBuffer '" << bufferId << "' count '" << outputBuffers.size() << "', found 1 dangling buffer !" << std::endl << std::flush; it = outputBuffers.erase(it); } else if (use == 1) { if (buf == nullptr) { it->age = 1; //select this one. buf = it->ptr; // std::cout << "**" << std::flush; it++; } else { //make the other unused buffers age it->age--; it++; } } else { it++; } } //end while //2.1 Garbage collect the oldest (last element) if it aged too much, and return the buffer if (buf != nullptr) { if (outputBuffers.back().age < -REBUFFER_GC_LIMIT) { //by the nature of the shared_ptr, memory will ne deallocated automatically. outputBuffers.pop_back(); //std::cout << "--" << std::flush; } return buf; } if (outputBuffers.size() > REBUFFER_WARNING_THRESHOLD) { std::cout << "Warning: ReBuffer '" << bufferId << "' count '" << outputBuffers.size() << "' exceeds threshold of '" << REBUFFER_WARNING_THRESHOLD << "'" << std::endl << std::flush; } //3.We need to allocate a new buffer. ReBufferAge < ReBufferPtr > newBuffer(std::make_shared(), 1); outputBuffers.push_back(newBuffer); // std::cout << "++" << std::flush; return newBuffer.ptr; } /// Purge the cache. void purge() { std::lock_guard < std::mutex > lock(m_mutex); // since outputBuffers are full std::shared_ptr, //purging if will effectively loose the local reference, // so the std::shared_ptr will naturally be deallocated //when their time comes. outputBuffers.clear(); } private: //name of the buffer cache kind std::string bufferId; //the ReBuffer cache: use a std:deque to also release //memory when ReBufferPtr are GCed. std::deque< ReBufferAge < ReBufferPtr > > outputBuffers; typedef typename std::deque< ReBufferAge < ReBufferPtr > >::iterator outputBuffersI; //mutex protecting access to outputBuffers. std::mutex m_mutex; }; class IOThread { public: IOThread(); virtual ~IOThread(); static void *pthread_helper(void *context); #ifdef __APPLE__ virtual void *threadMain(); #else //the thread Main call back itself virtual void threadMain(); #endif virtual void setup(); virtual void run(); //Request for termination (asynchronous) virtual void terminate(); //Returns true if the thread is indeed terminated, i.e the run() method //has returned. //If wait > 0 ms, the call is blocking at most 'waitMs' milliseconds for the thread to die, then returns. //If wait < 0, the wait in infinite until the thread dies. bool isTerminated(int waitMs = 0); virtual void onBindOutput(std::string name, ThreadQueueBasePtr threadQueue); virtual void onBindInput(std::string name, ThreadQueueBasePtr threadQueue); void setInputQueue(std::string qname, ThreadQueueBasePtr threadQueue); ThreadQueueBasePtr getInputQueue(std::string qname); void setOutputQueue(std::string qname, ThreadQueueBasePtr threadQueue); ThreadQueueBasePtr getOutputQueue(std::string qname); protected: std::map input_queues; std::map output_queues; //this protects against concurrent changes in input/output bindings: get/set/Input/OutPutQueue std::mutex m_queue_bindings_mutex; //true when a termination is ordered std::atomic_bool stopping; Timer gTimer; private: //true when the thread has really ended, i.e run() from threadMain() has returned. std::atomic_bool terminated; }; CubicSDR-0.2.3/src/ModemProperties.cpp000066400000000000000000000231271322677621400175420ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemProperties.h" #include "CubicSDR.h" ModemProperties::ModemProperties(wxWindow *parent, wxWindowID winid, const wxPoint& pos, const wxSize& size, long style, const wxString& name) : wxPanel(parent, winid, pos, size, style, name) { m_propertyGrid = new wxPropertyGrid(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_DEFAULT_STYLE); bSizer = new wxBoxSizer( wxVERTICAL ); bSizer->Add(m_propertyGrid, 1, wxEXPAND, 0); this->SetSizer(bSizer); m_propertyGrid->Connect( wxEVT_PG_ITEM_COLLAPSED, wxPropertyGridEventHandler( ModemProperties::OnCollapse ), NULL, this ); m_propertyGrid->Connect( wxEVT_PG_ITEM_EXPANDED, wxPropertyGridEventHandler( ModemProperties::OnExpand ), NULL, this ); m_propertyGrid->Connect( wxEVT_PG_CHANGED, wxPropertyGridEventHandler( ModemProperties::OnChange ), NULL, this ); this->Connect( wxEVT_SHOW, wxShowEventHandler( ModemProperties::OnShow ), NULL, this ); this->Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( ModemProperties::OnMouseEnter ), NULL, this); this->Connect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( ModemProperties::OnMouseLeave ), NULL, this); updateTheme(); mouseInView = false; collapsed = false; } void ModemProperties::OnShow(wxShowEvent & /* event */) { } void ModemProperties::updateTheme() { wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); wxColour textColor(ThemeMgr::mgr.currentTheme->text); wxColour btn(ThemeMgr::mgr.currentTheme->button); wxColour btnHl(ThemeMgr::mgr.currentTheme->buttonHighlight); m_propertyGrid->SetEmptySpaceColour(bgColor); m_propertyGrid->SetCellBackgroundColour(bgColor); m_propertyGrid->SetCellTextColour(textColor); m_propertyGrid->SetSelectionTextColour(bgColor); m_propertyGrid->SetSelectionBackgroundColour(btnHl); m_propertyGrid->SetCaptionTextColour(bgColor); m_propertyGrid->SetCaptionBackgroundColour(btn); m_propertyGrid->SetLineColour(btn); } ModemProperties::~ModemProperties() { } void ModemProperties::initDefaultProperties() { if (!audioOutputDevices.size()) { std::vector outputOpts; std::vector outputOptNames; AudioThread::enumerateDevices(audioDevices); int i = 0; for (auto aDev : audioDevices) { if (aDev.inputChannels) { audioInputDevices[i] = aDev; } if (aDev.outputChannels) { audioOutputDevices[i] = aDev; } i++; } int defaultDevice = 0; int dc = 0; for (auto mdevices_i : audioOutputDevices) { outputOpts.push_back(std::to_string(mdevices_i.first)); outputOptNames.push_back(mdevices_i.second.name); if (mdevices_i.second.isDefaultOutput) { defaultDevice = dc; } dc++; } outputArg.key ="._audio_output"; outputArg.name = "Audio Out"; outputArg.description = "Set the current modem's audio output device."; outputArg.type = ModemArgInfo::STRING; outputArg.options = outputOpts; outputArg.optionNames = outputOptNames; } int currentOutput = demodContext->getOutputDevice(); outputArg.value = std::to_string(currentOutput); defaultProps["._audio_output"] = addArgInfoProperty(m_propertyGrid, outputArg); } void ModemProperties::initProperties(ModemArgInfoList newArgs, DemodulatorInstancePtr demodInstance) { args = newArgs; demodContext = demodInstance; bSizer->Layout(); m_propertyGrid->Clear(); if (!demodInstance) { m_propertyGrid->Append(new wxPropertyCategory("Modem Settings")); return; } m_propertyGrid->Append(new wxPropertyCategory(demodInstance->getDemodulatorType() + " Settings")); initDefaultProperties(); ModemArgInfoList::const_iterator args_i; for (args_i = args.begin(); args_i != args.end(); args_i++) { ModemArgInfo arg = (*args_i); props[arg.key] = addArgInfoProperty(m_propertyGrid, arg); } m_propertyGrid->FitColumns(); if (collapsed) { m_propertyGrid->CollapseAll(); } } wxPGProperty *ModemProperties::addArgInfoProperty(wxPropertyGrid *pg, ModemArgInfo arg) { wxPGProperty *prop = nullptr; int intVal; double floatVal; std::vector::iterator stringIter; switch (arg.type) { case ModemArgInfo::INT: try { intVal = std::stoi(arg.value); } catch (std::invalid_argument e) { intVal = 0; } prop = pg->Append( new wxIntProperty(arg.name, wxPG_LABEL, intVal) ); if (arg.range.minimum() != arg.range.maximum()) { pg->SetPropertyAttribute( prop, wxPG_ATTR_MIN, arg.range.minimum()); pg->SetPropertyAttribute( prop, wxPG_ATTR_MAX, arg.range.maximum()); } break; case ModemArgInfo::FLOAT: try { floatVal = std::stod(arg.value); } catch (std::invalid_argument e) { floatVal = 0; } prop = pg->Append( new wxFloatProperty(arg.name, wxPG_LABEL, floatVal) ); if (arg.range.minimum() != arg.range.maximum()) { pg->SetPropertyAttribute( prop, wxPG_ATTR_MIN, arg.range.minimum()); pg->SetPropertyAttribute( prop, wxPG_ATTR_MAX, arg.range.maximum()); } break; case ModemArgInfo::BOOL: prop = pg->Append( new wxBoolProperty(arg.name, wxPG_LABEL, (arg.value=="true")) ); break; case ModemArgInfo::STRING: if (arg.options.size()) { intVal = 0; prop = pg->Append( new wxEnumProperty(arg.name, wxPG_LABEL) ); for (stringIter = arg.options.begin(); stringIter != arg.options.end(); stringIter++) { std::string optName = (*stringIter); std::string displayName = optName; if (arg.optionNames.size()) { displayName = arg.optionNames[intVal]; } prop->AddChoice(displayName); if ((*stringIter)==arg.value) { prop->SetChoiceSelection(intVal); } intVal++; } } else { prop = pg->Append( new wxStringProperty(arg.name, wxPG_LABEL, arg.value) ); } break; case ModemArgInfo::PATH_DIR: break; case ModemArgInfo::PATH_FILE: break; case ModemArgInfo::COLOR: break; } if (prop != NULL) { prop->SetHelpString(arg.name + ": " + arg.description); } return prop; } std::string ModemProperties::readProperty(std::string key) { int i = 0; ModemArgInfoList::const_iterator args_i; for (args_i = args.begin(); args_i != args.end(); args_i++) { ModemArgInfo arg = (*args_i); if (arg.key == key) { wxPGProperty *prop = props[key]; std::string result = ""; if (arg.type == ModemArgInfo::STRING && arg.options.size()) { return arg.options[prop->GetChoiceSelection()]; } else if (arg.type == ModemArgInfo::BOOL) { return (prop->GetValueAsString()=="True")?"true":"false"; } else { return prop->GetValueAsString().ToStdString(); } } i++; } return ""; } void ModemProperties::OnChange(wxPropertyGridEvent &event) { if (!demodContext || !demodContext->isActive()) { return; } std::map::const_iterator prop_i; if (event.m_property == defaultProps["._audio_output"]) { int sel = event.m_property->GetChoiceSelection(); outputArg.value = outputArg.options[sel]; if (demodContext) { try { demodContext->setOutputDevice(std::stoi(outputArg.value)); } catch (exception e) { // .. this should never happen ;) } wxGetApp().getAppFrame()->setScopeDeviceName(outputArg.optionNames[sel]); } return; } for (prop_i = props.begin(); prop_i != props.end(); prop_i++) { if (prop_i->second == event.m_property) { std::string key = prop_i->first; std::string value = readProperty(prop_i->first); demodContext->writeModemSetting(key, value); return; } } } void ModemProperties::OnCollapse(wxPropertyGridEvent &event) { collapsed = true; } void ModemProperties::OnExpand(wxPropertyGridEvent &event) { collapsed = false; } void ModemProperties::OnMouseEnter(wxMouseEvent & /* event */) { mouseInView = true; } void ModemProperties::OnMouseLeave(wxMouseEvent & /* event */) { mouseInView = false; } bool ModemProperties::isMouseInView() { return mouseInView || (m_propertyGrid && m_propertyGrid->IsEditorFocused()); } void ModemProperties::setCollapsed(bool state) { collapsed = state; if (m_propertyGrid) { if (state) { m_propertyGrid->CollapseAll(); } else { m_propertyGrid->ExpandAll(); } } } bool ModemProperties::isCollapsed() { return collapsed; } void ModemProperties::fitColumns() { m_propertyGrid->FitColumns(); } CubicSDR-0.2.3/src/ModemProperties.h000066400000000000000000000033721322677621400172070ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include "DemodulatorInstance.h" #include "Modem.h" class ModemProperties : public wxPanel { public: ModemProperties( wxWindow *parent, wxWindowID winid = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL | wxNO_BORDER, const wxString& name = wxPanelNameStr ); ~ModemProperties(); void initDefaultProperties(); void initProperties(ModemArgInfoList newArgs, DemodulatorInstancePtr demodInstance); bool isMouseInView(); void setCollapsed(bool state); bool isCollapsed(); void fitColumns(); void updateTheme(); private: wxPGProperty *addArgInfoProperty(wxPropertyGrid *pg, ModemArgInfo arg); std::string readProperty(std::string); void OnChange(wxPropertyGridEvent &event); void OnShow(wxShowEvent &event); void OnCollapse(wxPropertyGridEvent &event); void OnExpand(wxPropertyGridEvent &event); void OnMouseEnter(wxMouseEvent &event); void OnMouseLeave(wxMouseEvent &event); wxBoxSizer* bSizer; wxPropertyGrid* m_propertyGrid; ModemArgInfoList args; DemodulatorInstancePtr demodContext; std::map props; bool mouseInView, collapsed; ModemArgInfoList defaultArgs; ModemArgInfo outputArg; std::map defaultProps; std::vector audioDevices; std::map audioInputDevices; std::map audioOutputDevices; };CubicSDR-0.2.3/src/audio/000077500000000000000000000000001322677621400150145ustar00rootroot00000000000000CubicSDR-0.2.3/src/audio/AudioFile.cpp000066400000000000000000000024231322677621400173620ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "AudioFile.h" #include "CubicSDR.h" #include AudioFile::AudioFile() { } AudioFile::~AudioFile() { } void AudioFile::setOutputFileName(std::string filename) { filenameBase = filename; } std::string AudioFile::getOutputFileName() { std::string recPath = wxGetApp().getConfig()->getRecordingPath(); // Strip any invalid characters from the name std::string stripChars("<>:\"/\\|?*"); std::string filenameBaseSafe = filenameBase; for (size_t i = 0, iMax = filenameBaseSafe.length(); i < iMax; i++) { if (stripChars.find(filenameBaseSafe[i]) != std::string::npos) { filenameBaseSafe.replace(i,1,"_"); } } // Create output file name std::stringstream outputFileName; outputFileName << recPath << filePathSeparator << filenameBaseSafe; int idx = 0; // If the file exists; then find the next non-existing file in sequence. std::string fileNameCandidate = outputFileName.str(); while (FILE *file = fopen((fileNameCandidate + "." + getExtension()).c_str(), "r")) { fclose(file); fileNameCandidate = outputFileName.str() + "-" + std::to_string(++idx); } return fileNameCandidate + "." + getExtension(); } CubicSDR-0.2.3/src/audio/AudioFile.h000066400000000000000000000007301322677621400170260ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "AudioThread.h" class AudioFile { public: AudioFile(); virtual ~AudioFile(); virtual void setOutputFileName(std::string filename); virtual std::string getExtension() = 0; virtual std::string getOutputFileName(); virtual bool writeToFile(AudioThreadInputPtr input) = 0; virtual bool closeFile() = 0; protected: std::string filenameBase; };CubicSDR-0.2.3/src/audio/AudioFileWAV.cpp000066400000000000000000000147151322677621400177470ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "AudioFileWAV.h" #include "CubicSDR.h" #include //limit file size to 2GB (- margin) for maximum compatibility. #define MAX_WAV_FILE_SIZE (0x7FFFFFFF - 1024) // Simple endian io read/write handling from // http://www.cplusplus.com/forum/beginner/31584/#msg171056 namespace little_endian_io { template std::ostream& write_word(std::ostream& outs, Word value, unsigned size = sizeof(Word)) { for (; size; --size, value >>= 8) { outs.put(static_cast (value & 0xFF)); } return outs; } template std::istream& read_word(std::istream& ins, Word& value, unsigned size = sizeof(Word)) { for (unsigned n = 0, value = 0; n < size; ++n) { value |= ins.get() << (8 * n); } return ins; } } namespace big_endian_io { template std::ostream& write_word(std::ostream& outs, Word value, unsigned size = sizeof(Word)) { while (size) { outs.put(static_cast ((value >> (8 * --size)) & 0xFF)); } return outs; } template std::istream& read_word(std::istream& ins, Word& value, unsigned size = sizeof(Word)) { for (value = 0; size; --size) { value = (value << 8) | ins.get(); } return ins; } } using namespace little_endian_io; AudioFileWAV::AudioFileWAV() : AudioFile() { } AudioFileWAV::~AudioFileWAV() { } std::string AudioFileWAV::getExtension() { return "wav"; } bool AudioFileWAV::writeToFile(AudioThreadInputPtr input) { if (!outputFileStream.is_open()) { std::string ofName = getOutputFileName(); outputFileStream.open(ofName.c_str(), std::ios::binary); currentFileSize = 0; writeHeaderToFileStream(input); } size_t maxRoomInCurrentFileInSamples = getMaxWritableNumberOfSamples(input); if (maxRoomInCurrentFileInSamples >= input->data.size()) { writePayloadToFileStream(input, 0, input->data.size()); } else { //we complete the current file and open another: writePayloadToFileStream(input, 0, maxRoomInCurrentFileInSamples); closeFile(); // Open a new file with the next sequence number, and dump the rest of samples in it. currentSequenceNumber++; currentFileSize = 0; std::string ofName = getOutputFileName(); outputFileStream.open(ofName.c_str(), std::ios::binary); writeHeaderToFileStream(input); writePayloadToFileStream(input, maxRoomInCurrentFileInSamples, input->data.size()); } return true; } bool AudioFileWAV::closeFile() { if (outputFileStream.is_open()) { size_t file_length = outputFileStream.tellp(); // Fix the data chunk header to contain the data size outputFileStream.seekp(dataChunkPos + 4); write_word(outputFileStream, file_length - dataChunkPos + 8); // Fix the file header to contain the proper RIFF chunk size, which is (file size - 8) bytes outputFileStream.seekp(0 + 4); write_word(outputFileStream, file_length - 8, 4); outputFileStream.close(); currentFileSize = 0; } return true; } void AudioFileWAV::writeHeaderToFileStream(AudioThreadInputPtr input) { // Based on simple wav file output code from // http://www.cplusplus.com/forum/beginner/166954/ // Write the wav file headers outputFileStream << "RIFF----WAVEfmt "; // (chunk size to be filled in later) write_word(outputFileStream, 16, 4); // no extension data write_word(outputFileStream, 1, 2); // PCM - integer samples write_word(outputFileStream, input->channels, 2); // channels write_word(outputFileStream, input->sampleRate, 4); // samples per second (Hz) write_word(outputFileStream, (input->sampleRate * 16 * input->channels) / 8, 4); // (Sample Rate * BitsPerSample * Channels) / 8 write_word(outputFileStream, input->channels * 2, 2); // data block size (size of integer samples, one for each channel, in bytes) write_word(outputFileStream, 16, 2); // number of bits per sample (use a multiple of 8) // Write the data chunk header dataChunkPos = outputFileStream.tellp(); currentFileSize = dataChunkPos; outputFileStream << "data----"; // (chunk size to be filled in later) } void AudioFileWAV::writePayloadToFileStream(AudioThreadInputPtr input, size_t startInputPosition, size_t endInputPosition) { // Prevent clipping float intScale = (input->peak < 1.0) ? 32767.0f : (32767.0f / input->peak); if (input->channels == 1) { for (size_t i = startInputPosition, iMax = endInputPosition; i < iMax; i++) { write_word(outputFileStream, int(input->data[i] * intScale), 2); currentFileSize += 2; } } else if (input->channels == 2) { for (size_t i = startInputPosition, iMax = endInputPosition / 2; i < iMax; i++) { write_word(outputFileStream, int(input->data[i * 2] * intScale), 2); write_word(outputFileStream, int(input->data[i * 2 + 1] * intScale), 2); currentFileSize += 4; } } } size_t AudioFileWAV::getMaxWritableNumberOfSamples(AudioThreadInputPtr input) { long long remainingBytesInFile = (long long)(MAX_WAV_FILE_SIZE) - currentFileSize; return (size_t)(remainingBytesInFile / (input->channels * 2)); } void AudioFileWAV::setOutputFileName(std::string filename) { if (filename != filenameBase) { currentSequenceNumber = 0; } AudioFile::setOutputFileName(filename); } std::string AudioFileWAV::getOutputFileName() { std::string recPath = wxGetApp().getConfig()->getRecordingPath(); // Strip any invalid characters from the name std::string stripChars("<>:\"/\\|?*"); std::string filenameBaseSafe = filenameBase; for (size_t i = 0, iMax = filenameBaseSafe.length(); i < iMax; i++) { if (stripChars.find(filenameBaseSafe[i]) != std::string::npos) { filenameBaseSafe.replace(i, 1, "_"); } } // Create output file name std::stringstream outputFileName; outputFileName << recPath << filePathSeparator << filenameBaseSafe; //customized part: append a sequence number. if (currentSequenceNumber > 0) { outputFileName << "_" << std::setfill('0') << std::setw(3) << currentSequenceNumber; } int idx = 0; // If the file exists; then find the next non-existing file in sequence. std::string fileNameCandidate = outputFileName.str(); while (FILE *file = fopen((fileNameCandidate + "." + getExtension()).c_str(), "r")) { fclose(file); fileNameCandidate = outputFileName.str() + "-" + std::to_string(++idx); } return fileNameCandidate + "." + getExtension(); }CubicSDR-0.2.3/src/audio/AudioFileWAV.h000066400000000000000000000020631322677621400174050ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "AudioFile.h" #include class AudioFileWAV : public AudioFile { public: AudioFileWAV(); ~AudioFileWAV(); //override to manage name change with multi-part WAV. virtual void setOutputFileName(std::string filename); //override of the base method to generate multi-part //WAV to overcome the WAV format size limit. virtual std::string getOutputFileName(); virtual std::string getExtension(); virtual bool writeToFile(AudioThreadInputPtr input); virtual bool closeFile(); protected: std::ofstream outputFileStream; size_t dataChunkPos; long long currentFileSize = 0; int currentSequenceNumber = 0; private: size_t getMaxWritableNumberOfSamples(AudioThreadInputPtr input); void writeHeaderToFileStream(AudioThreadInputPtr input); //write [startInputPosition; endInputPosition[ samples from input into the file. void writePayloadToFileStream(AudioThreadInputPtr input, size_t startInputPosition, size_t endInputPosition); };CubicSDR-0.2.3/src/audio/AudioSinkFileThread.cpp000066400000000000000000000070751322677621400213470ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "AudioSinkFileThread.h" #include #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) AudioSinkFileThread::AudioSinkFileThread() : AudioSinkThread() { } AudioSinkFileThread::~AudioSinkFileThread() { if (audioFileHandler != nullptr) { audioFileHandler->closeFile(); } } void AudioSinkFileThread::sink(AudioThreadInputPtr input) { if (!audioFileHandler) { return; } //by default, always write something bool isSomethingToWrite = true; if (input->is_squelch_active) { if (squelchOption == SQUELCH_RECORD_SILENCE) { //patch with "silence" input->data.assign(input->data.size(), 0.0f); input->peak = 0.0f; } else if (squelchOption == SQUELCH_SKIP_SILENCE) { isSomethingToWrite = false; } } //else, nothing to do record as if squelch was not enabled. if (!isSomethingToWrite) { return; } if (fileTimeLimit > 0) { durationMeasurement.update(); //duration exeeded, close this file and create another //with "now" as timestamp. if (durationMeasurement.getSeconds() > fileTimeLimit) { audioFileHandler->closeFile(); //initialize the filename of the AudioFile with the current time time_t t = std::time(nullptr); tm ltm = *std::localtime(&t); // GCC 5+ // fileName << "_" << std::put_time(<m, "%d-%m-%Y_%H-%M-%S"); char timeStr[512]; //International format: Year.Month.Day, also lexicographically sortable strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", <m); audioFileHandler->setOutputFileName(fileNameBase + std::string("_") + timeStr); //reset duration counter durationMeasurement.start(); //the following writeToFile will take care of creating another file. } } // forward to output file handler audioFileHandler->writeToFile(input); } void AudioSinkFileThread::inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) { // close, set new parameters, adjust file name sequence and re-open? if (!audioFileHandler) { return; } audioFileHandler->closeFile(); //reset duration counter durationMeasurement.start(); } void AudioSinkFileThread::setAudioFileNameBase(const std::string& baseName) { fileNameBase = baseName; } void AudioSinkFileThread::setAudioFileHandler(AudioFile * output) { audioFileHandler = output; //initialize the filename of the AudioFile with the current time time_t t = std::time(nullptr); tm ltm = *std::localtime(&t); // GCC 5+ // fileName << "_" << std::put_time(<m, "%d-%m-%Y_%H-%M-%S"); char timeStr[512]; //International format: Year.Month.Day, also lexicographically sortable strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", <m); audioFileHandler->setOutputFileName(fileNameBase + std::string("_") + timeStr); // reset Timer durationMeasurement.start(); } void AudioSinkFileThread::setSquelchOption(int squelchOptEnumValue) { if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_RECORD_SILENCE) { squelchOption = AudioSinkFileThread::SQUELCH_RECORD_SILENCE; } else if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_SKIP_SILENCE) { squelchOption = AudioSinkFileThread::SQUELCH_SKIP_SILENCE; } else if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_RECORD_ALWAYS) { squelchOption = AudioSinkFileThread::SQUELCH_RECORD_ALWAYS; } else { squelchOption = AudioSinkFileThread::SQUELCH_RECORD_SILENCE; } } // Time limit void AudioSinkFileThread::setFileTimeLimit(int nbSeconds) { if (nbSeconds > 0) { fileTimeLimit = nbSeconds; } else { fileTimeLimit = 0; } } CubicSDR-0.2.3/src/audio/AudioSinkFileThread.h000066400000000000000000000021571322677621400210100ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "AudioSinkThread.h" #include "AudioFile.h" #include "Timer.h" class AudioSinkFileThread : public AudioSinkThread { public: AudioSinkFileThread(); ~AudioSinkFileThread(); enum SquelchOption { SQUELCH_RECORD_SILENCE = 0, // default value, record as a user would hear it. SQUELCH_SKIP_SILENCE = 1, // skip below-squelch level. SQUELCH_RECORD_ALWAYS = 2, // record irrespective of the squelch level. SQUELCH_RECORD_MAX }; virtual void sink(AudioThreadInputPtr input); virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps); void setAudioFileHandler(AudioFile *output); void setAudioFileNameBase(const std::string& baseName); //Squelch void setSquelchOption(int squelchOptEnumValue); // Time limit void setFileTimeLimit(int nbSeconds); protected: std::string fileNameBase; AudioFile *audioFileHandler = nullptr; SquelchOption squelchOption = SQUELCH_RECORD_SILENCE; int fileTimeLimit = 0; int fileTimeDurationSeconds = -1; Timer durationMeasurement; }; CubicSDR-0.2.3/src/audio/AudioSinkThread.cpp000066400000000000000000000026611322677621400205430ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "AudioSinkThread.h" #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) AudioSinkThread::AudioSinkThread() { inputQueuePtr = std::make_shared(); inputQueuePtr->set_max_num_items(1000); setInputQueue("input", inputQueuePtr); } AudioSinkThread::~AudioSinkThread() { } void AudioSinkThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread int priority = sched_get_priority_max(SCHED_RR) - 1; sched_param prio = { priority }; // scheduling priority of thread pthread_setschedparam(tID, SCHED_RR, &prio); #endif AudioThreadInputPtr inp; AudioThreadInput inputRef; while (!stopping) { if (!inputQueuePtr->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) { continue; } if (inputRef.channels != inp->channels || inputRef.frequency != inp->frequency || inputRef.inputRate != inp->inputRate || inputRef.sampleRate != inp->sampleRate) { inputChanged(inputRef, inp); inputRef.channels = inp->channels; inputRef.frequency = inp->frequency; inputRef.inputRate = inp->inputRate; inputRef.sampleRate = inp->sampleRate; } sink(inp); } } void AudioSinkThread::terminate() { IOThread::terminate(); inputQueuePtr->flush(); } CubicSDR-0.2.3/src/audio/AudioSinkThread.h000066400000000000000000000007761322677621400202150ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "AudioThread.h" class AudioSinkThread : public IOThread { public: AudioSinkThread(); virtual ~AudioSinkThread(); virtual void run(); virtual void terminate(); virtual void sink(AudioThreadInputPtr input) = 0; virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) = 0; protected: std::recursive_mutex m_mutex; AudioThreadInputQueuePtr inputQueuePtr; }; CubicSDR-0.2.3/src/audio/AudioThread.cpp000066400000000000000000000410271322677621400177150ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "AudioThread.h" #include "CubicSDRDefs.h" #include #include #include "CubicSDR.h" #include "DemodulatorThread.h" #include "DemodulatorInstance.h" #include #include //50 ms #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) std::map AudioThread::deviceController; std::map AudioThread::deviceSampleRate; std::map AudioThread::deviceThread; std::recursive_mutex AudioThread::m_device_mutex; AudioThread::AudioThread() : IOThread(), nBufferFrames(1024), sampleRate(0) { audioQueuePtr = 0; underflowCount = 0; active.store(false); outputDevice.store(-1); gain = 1.0; } AudioThread::~AudioThread() { std::lock_guard lock(m_mutex); } std::recursive_mutex & AudioThread::getMutex() { return m_mutex; } void AudioThread::bindThread(AudioThread *other) { std::lock_guard lock(m_mutex); if (std::find(boundThreads.begin(), boundThreads.end(), other) == boundThreads.end()) { boundThreads.push_back(other); } } void AudioThread::removeThread(AudioThread *other) { std::lock_guard lock(m_mutex); auto i = std::find(boundThreads.begin(), boundThreads.end(), other); if (i != boundThreads.end()) { boundThreads.erase(i); } } void AudioThread::deviceCleanup() { std::lock_guard lock(m_device_mutex); for (auto i = deviceController.begin(); i != deviceController.end(); i++) { i->second->terminate(); } } static int audioCallback(void *outputBuffer, void * /* inputBuffer */, unsigned int nBufferFrames, double /* streamTime */, RtAudioStreamStatus status, void *userData) { float *out = (float*)outputBuffer; //Zero output buffer in all cases: this allow to mute audio if no AudioThread data is //actually active. ::memset(out, 0, nBufferFrames * 2 * sizeof(float)); AudioThread *src = (AudioThread *) userData; std::lock_guard lock(src->getMutex()); if (src->isTerminated()) { return 1; } if (status) { std::cout << "Audio buffer underflow.." << (src->underflowCount++) << std::endl << std::flush; } if (src->boundThreads.empty()) { return 0; } double peak = 0.0; //for all boundThreads for (size_t j = 0; j < src->boundThreads.size(); j++) { AudioThread *srcmix = src->boundThreads[j]; //lock every single boundThread srcmix in succession the time we process //its audio samples. std::lock_guard lock(srcmix->getMutex()); if (srcmix->isTerminated() || !srcmix->inputQueue || srcmix->inputQueue->empty() || !srcmix->isActive()) { continue; } if (!srcmix->currentInput) { srcmix->audioQueuePtr = 0; if (!srcmix->inputQueue->try_pop(srcmix->currentInput)) { continue; } continue; } if (srcmix->currentInput->sampleRate != src->getSampleRate()) { while (srcmix->inputQueue->try_pop(srcmix->currentInput)) { if (srcmix->currentInput) { if (srcmix->currentInput->sampleRate == src->getSampleRate()) { break; } } srcmix->currentInput = nullptr; } //end while srcmix->audioQueuePtr = 0; if (!srcmix->currentInput) { continue; } } if (srcmix->currentInput->channels == 0 || !srcmix->currentInput->data.size()) { if (!srcmix->inputQueue->empty()) { srcmix->audioQueuePtr = 0; if (srcmix->currentInput) { srcmix->currentInput = nullptr; } if (!srcmix->inputQueue->try_pop(srcmix->currentInput)) { continue; } } continue; } double mixPeak = srcmix->currentInput->peak * srcmix->gain; if (srcmix->currentInput->channels == 1) { for (unsigned int i = 0; i < nBufferFrames; i++) { if (srcmix->audioQueuePtr >= srcmix->currentInput->data.size()) { srcmix->audioQueuePtr = 0; if (srcmix->currentInput) { srcmix->currentInput = nullptr; } if (!srcmix->inputQueue->try_pop(srcmix->currentInput)) { break; } double srcPeak = srcmix->currentInput->peak * srcmix->gain; if (mixPeak < srcPeak) { mixPeak = srcPeak; } } if (srcmix->currentInput && srcmix->currentInput->data.size()) { float v = srcmix->currentInput->data[srcmix->audioQueuePtr] * srcmix->gain; out[i * 2] += v; out[i * 2 + 1] += v; } srcmix->audioQueuePtr++; } } else { for (int i = 0, iMax = srcmix->currentInput->channels * nBufferFrames; i < iMax; i++) { if (srcmix->audioQueuePtr >= srcmix->currentInput->data.size()) { srcmix->audioQueuePtr = 0; if (srcmix->currentInput) { srcmix->currentInput = nullptr; } if (!srcmix->inputQueue->try_pop(srcmix->currentInput)) { break; } double srcPeak = srcmix->currentInput->peak * srcmix->gain; if (mixPeak < srcPeak) { mixPeak = srcPeak; } } if (srcmix->currentInput && srcmix->currentInput->data.size()) { out[i] = out[i] + srcmix->currentInput->data[srcmix->audioQueuePtr] * srcmix->gain; } srcmix->audioQueuePtr++; } } peak += mixPeak; } //normalize volume if (peak > 1.0) { float invPeak = (float)(1.0 / peak); for (unsigned int i = 0; i < nBufferFrames * 2; i++) { out[i] *= invPeak; } } return 0; } void AudioThread::enumerateDevices(std::vector &devs) { RtAudio endac; int numDevices = endac.getDeviceCount(); for (int i = 0; i < numDevices; i++) { RtAudio::DeviceInfo info = endac.getDeviceInfo(i); devs.push_back(info); std::cout << std::endl; std::cout << "Audio Device #" << i << " " << info.name << std::endl; std::cout << "\tDefault Output? " << (info.isDefaultOutput ? "Yes" : "No") << std::endl; std::cout << "\tDefault Input? " << (info.isDefaultInput ? "Yes" : "No") << std::endl; std::cout << "\tInput channels: " << info.inputChannels << std::endl; std::cout << "\tOutput channels: " << info.outputChannels << std::endl; std::cout << "\tDuplex channels: " << info.duplexChannels << std::endl; std::cout << "\t" << "Native formats:" << std::endl; RtAudioFormat nFormats = info.nativeFormats; if (nFormats & RTAUDIO_SINT8) { std::cout << "\t\t8-bit signed integer." << std::endl; } if (nFormats & RTAUDIO_SINT16) { std::cout << "\t\t16-bit signed integer." << std::endl; } if (nFormats & RTAUDIO_SINT24) { std::cout << "\t\t24-bit signed integer." << std::endl; } if (nFormats & RTAUDIO_SINT32) { std::cout << "\t\t32-bit signed integer." << std::endl; } if (nFormats & RTAUDIO_FLOAT32) { std::cout << "\t\t32-bit float normalized between plus/minus 1.0." << std::endl; } if (nFormats & RTAUDIO_FLOAT64) { std::cout << "\t\t64-bit float normalized between plus/minus 1.0." << std::endl; } std::vector::iterator srate; std::cout << "\t" << "Supported sample rates:" << std::endl; for (srate = info.sampleRates.begin(); srate != info.sampleRates.end(); srate++) { std::cout << "\t\t" << (*srate) << "hz" << std::endl; } std::cout << std::endl; } } void AudioThread::setDeviceSampleRate(int deviceId, int sampleRate) { AudioThread* matchingAudioThread = nullptr; //scope lock here to minimize the common unique static lock contention { std::lock_guard lock(m_device_mutex); if (deviceController.find(deviceId) != deviceController.end()) { matchingAudioThread = deviceController[deviceId]; } } //out-of-lock test if (matchingAudioThread != nullptr) { AudioThreadCommand refreshDevice; refreshDevice.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_SAMPLE_RATE; refreshDevice.int_value = sampleRate; //VSO : blocking push ! matchingAudioThread->getCommandQueue()->push(refreshDevice); } } void AudioThread::setSampleRate(int sampleRate) { bool outputIsThis = false; //scope lock here to minimize the common unique static lock contention { std::lock_guard lock(m_device_mutex); if (deviceController[outputDevice.load()] == this) { outputIsThis = true; deviceSampleRate[outputDevice.load()] = sampleRate; } } std::lock_guard lock(m_mutex); if (outputIsThis) { dac.stopStream(); dac.closeStream(); for (size_t j = 0; j < boundThreads.size(); j++) { AudioThread *srcmix = boundThreads[j]; srcmix->setSampleRate(sampleRate); } //make a local copy, snapshot of the list of demodulators std::vector demodulators = wxGetApp().getDemodMgr().getDemodulators(); for (auto demod : demodulators) { if (demod->getOutputDevice() == outputDevice.load()) { demod->setAudioSampleRate(sampleRate); } } dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &nBufferFrames, &audioCallback, (void *) this, &opts); dac.startStream(); } this->sampleRate = sampleRate; } int AudioThread::getSampleRate() { std::lock_guard lock(m_mutex); return this->sampleRate; } void AudioThread::setupDevice(int deviceId) { //global lock to setup the device... std::lock_guard lock(m_device_mutex); parameters.deviceId = deviceId; parameters.nChannels = 2; parameters.firstChannel = 0; opts.streamName = "CubicSDR Audio Output"; try { if (deviceController.find(outputDevice.load()) != deviceController.end()) { deviceController[outputDevice.load()]->removeThread(this); } #ifndef _MSC_VER opts.priority = sched_get_priority_max(SCHED_FIFO); #endif // opts.flags = RTAUDIO_MINIMIZE_LATENCY; opts.flags = RTAUDIO_SCHEDULE_REALTIME; if (deviceSampleRate.find(parameters.deviceId) != deviceSampleRate.end()) { sampleRate = deviceSampleRate[parameters.deviceId]; } else { std::cout << "Error, device sample rate wasn't initialized?" << std::endl; return; // sampleRate = AudioThread::getDefaultAudioSampleRate(); // deviceSampleRate[parameters.deviceId] = sampleRate; } if (deviceController.find(parameters.deviceId) == deviceController.end()) { deviceController[parameters.deviceId] = new AudioThread(); deviceController[parameters.deviceId]->setInitOutputDevice(parameters.deviceId, sampleRate); deviceController[parameters.deviceId]->bindThread(this); deviceThread[parameters.deviceId] = new std::thread(&AudioThread::threadMain, deviceController[parameters.deviceId]); } else if (deviceController[parameters.deviceId] == this) { //Attach callback dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &nBufferFrames, &audioCallback, (void *) this, &opts); dac.startStream(); } else { deviceController[parameters.deviceId]->bindThread(this); } active = true; } catch (RtAudioError& e) { e.printMessage(); return; } if (deviceId != -1) { outputDevice = deviceId; } } int AudioThread::getOutputDevice() { std::lock_guard lock(m_mutex); if (outputDevice == -1) { return dac.getDefaultOutputDevice(); } return outputDevice; } void AudioThread::setInitOutputDevice(int deviceId, int sampleRate) { //global lock std::lock_guard lock(m_device_mutex); outputDevice = deviceId; if (sampleRate == -1) { if (deviceSampleRate.find(parameters.deviceId) != deviceSampleRate.end()) { sampleRate = deviceSampleRate[deviceId]; } } else { deviceSampleRate[deviceId] = sampleRate; } this->sampleRate = sampleRate; } void AudioThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread int priority = sched_get_priority_max( SCHED_RR) - 1; sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_RR, &prio); #endif // std::cout << "Audio thread initializing.." << std::endl; if (dac.getDeviceCount() < 1) { std::cout << "No audio devices found!" << std::endl; return; } setupDevice((outputDevice.load() == -1) ? (dac.getDefaultOutputDevice()) : outputDevice.load()); // std::cout << "Audio thread started." << std::endl; inputQueue = std::static_pointer_cast(getInputQueue("AudioDataInput")); //Infinite loop, witing for commands or for termination while (!stopping) { AudioThreadCommand command; if (!cmdQueue.pop(command, HEARTBEAT_CHECK_PERIOD_MICROS)) { continue; } if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE) { setupDevice(command.int_value); } if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_SAMPLE_RATE) { setSampleRate(command.int_value); } } // Drain any remaining inputs, with a non-blocking pop if (inputQueue != nullptr) { inputQueue->flush(); } //Thread termination, prevent fancy things to happen, lock the whole thing: //This way audioThreadCallback is rightly protected from thread termination std::lock_guard lock(m_mutex); //Nullify currentInput... currentInput = nullptr; //Stop : this affects the device list , so must be protected globally. std::lock_guard global_lock(m_device_mutex); if (deviceController[parameters.deviceId] != this) { deviceController[parameters.deviceId]->removeThread(this); } else { try { if (dac.isStreamOpen()) { if (dac.isStreamRunning()) { dac.stopStream(); } dac.closeStream(); } } catch (RtAudioError& e) { e.printMessage(); } } // std::cout << "Audio thread done." << std::endl; } void AudioThread::terminate() { IOThread::terminate(); } bool AudioThread::isActive() { std::lock_guard lock(m_mutex); return active; } void AudioThread::setActive(bool state) { AudioThread* matchingAudioThread = nullptr; //scope lock here to minimize the common unique static lock contention { std::lock_guard lock(m_device_mutex); if (deviceController.find(parameters.deviceId) != deviceController.end()) { matchingAudioThread = deviceController[parameters.deviceId]; } } std::lock_guard lock(m_mutex); if (matchingAudioThread == nullptr) { return; } if (state && !active && inputQueue) { matchingAudioThread->bindThread(this); } else if (!state && active) { matchingAudioThread->removeThread(this); } // Activity state changing, clear any inputs if(inputQueue) { inputQueue->flush(); } active = state; } AudioThreadCommandQueue *AudioThread::getCommandQueue() { return &cmdQueue; } void AudioThread::setGain(float gain_in) { if (gain_in < 0.0) { gain_in = 0.0; } if (gain_in > 2.0) { gain_in = 2.0; } gain = gain_in; } CubicSDR-0.2.3/src/audio/AudioThread.h000066400000000000000000000073341322677621400173650ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include #include #include "ThreadBlockingQueue.h" #include "RtAudio.h" #include "DemodDefs.h" class AudioThreadInput { public: long long frequency; int inputRate; int sampleRate; int channels; float peak; int type; bool is_squelch_active; std::vector data; AudioThreadInput() : frequency(0), inputRate(0), sampleRate(0), channels(0), peak(0), type(0), is_squelch_active(false) { } AudioThreadInput(AudioThreadInput *copyFrom) { copy(copyFrom); } void copy(AudioThreadInput *copyFrom) { frequency = copyFrom->frequency; inputRate = copyFrom->inputRate; sampleRate = copyFrom->sampleRate; channels = copyFrom->channels; peak = copyFrom->peak; type = copyFrom->type; is_squelch_active = copyFrom->is_squelch_active; data.assign(copyFrom->data.begin(), copyFrom->data.end()); } virtual ~AudioThreadInput() { } }; typedef std::shared_ptr AudioThreadInputPtr; typedef ThreadBlockingQueue DemodulatorThreadOutputQueue; typedef std::shared_ptr DemodulatorThreadOutputQueuePtr; class AudioThreadCommand { public: enum AudioThreadCommandEnum { AUDIO_THREAD_CMD_NULL, AUDIO_THREAD_CMD_SET_DEVICE, AUDIO_THREAD_CMD_SET_SAMPLE_RATE }; AudioThreadCommand() : cmd(AUDIO_THREAD_CMD_NULL), int_value(0) { } AudioThreadCommandEnum cmd; int int_value; }; typedef ThreadBlockingQueue AudioThreadInputQueue; typedef ThreadBlockingQueue AudioThreadCommandQueue; typedef std::shared_ptr AudioThreadInputQueuePtr; typedef std::shared_ptr AudioThreadCommandQueuePtr; class AudioThread : public IOThread { public: AudioThread(); virtual ~AudioThread(); static void enumerateDevices(std::vector &devs); void setInitOutputDevice(int deviceId, int sampleRate=-1); int getOutputDevice(); int getSampleRate(); virtual void run(); virtual void terminate(); bool isActive(); void setActive(bool state); void setGain(float gain_in); static std::map deviceSampleRate; AudioThreadCommandQueue *getCommandQueue(); //give access to the this AudioThread lock std::recursive_mutex& getMutex(); static void deviceCleanup(); static void setDeviceSampleRate(int deviceId, int sampleRate); //fields below, only to be used by other AudioThreads ! size_t underflowCount; //protected by m_mutex std::vector boundThreads; AudioThreadInputQueuePtr inputQueue; AudioThreadInputPtr currentInput; size_t audioQueuePtr; float gain; private: std::atomic_bool active; std::atomic_int outputDevice; RtAudio dac; unsigned int nBufferFrames; RtAudio::StreamOptions opts; RtAudio::StreamParameters parameters; AudioThreadCommandQueue cmdQueue; int sampleRate; //The own m_mutex protecting this AudioThread, in particular boundThreads std::recursive_mutex m_mutex; void setupDevice(int deviceId); void setSampleRate(int sampleRate); void bindThread(AudioThread *other); void removeThread(AudioThread *other); static std::map deviceController; static std::map deviceThread; //The mutex protecting static deviceController, deviceThread and deviceSampleRate access. static std::recursive_mutex m_device_mutex; }; CubicSDR-0.2.3/src/demod/000077500000000000000000000000001322677621400150035ustar00rootroot00000000000000CubicSDR-0.2.3/src/demod/DemodDefs.h000066400000000000000000000054771322677621400170230ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ThreadBlockingQueue.h" #include "CubicSDRDefs.h" #include "liquid/liquid.h" #include #include #include #include #include "IOThread.h" class DemodulatorThread; class DemodulatorThreadControlCommand { public: enum DemodulatorThreadControlCommandEnum { DEMOD_THREAD_CMD_CTL_NULL, DEMOD_THREAD_CMD_CTL_SQUELCH_ON, DEMOD_THREAD_CMD_CTL_SQUELCH_OFF, DEMOD_THREAD_CMD_CTL_TYPE }; DemodulatorThreadControlCommand() : cmd(DEMOD_THREAD_CMD_CTL_NULL), demodType("") { } DemodulatorThreadControlCommandEnum cmd; std::string demodType; }; class DemodulatorThreadIQData { public: long long frequency; long long sampleRate; std::vector data; DemodulatorThreadIQData() : frequency(0), sampleRate(0) { } DemodulatorThreadIQData & operator=(const DemodulatorThreadIQData &other) { frequency = other.frequency; sampleRate = other.sampleRate; data.assign(other.data.begin(), other.data.end()); return *this; } virtual ~DemodulatorThreadIQData() { } }; class Modem; class ModemKit; class DemodulatorThreadPostIQData { public: std::vector data; long long sampleRate; std::string modemName; std::string modemType; Modem *modem; ModemKit *modemKit; DemodulatorThreadPostIQData() : sampleRate(0), modem(nullptr), modemKit(nullptr) { } virtual ~DemodulatorThreadPostIQData() { } }; class DemodulatorThreadAudioData { public: long long frequency; unsigned int sampleRate; unsigned char channels; std::vector *data; DemodulatorThreadAudioData() : frequency(0), sampleRate(0), channels(0), data(NULL) { } DemodulatorThreadAudioData(long long frequency, unsigned int sampleRate, std::vector *data) : frequency(frequency), sampleRate(sampleRate), channels(1), data(data) { } virtual ~DemodulatorThreadAudioData() { } }; typedef std::shared_ptr DemodulatorThreadIQDataPtr; typedef std::shared_ptr DemodulatorThreadPostIQDataPtr; typedef ThreadBlockingQueue DemodulatorThreadInputQueue; typedef ThreadBlockingQueue DemodulatorThreadPostInputQueue; typedef ThreadBlockingQueue DemodulatorThreadControlCommandQueue; typedef std::shared_ptr DemodulatorThreadInputQueuePtr; typedef std::shared_ptr DemodulatorThreadPostInputQueuePtr; typedef std::shared_ptr DemodulatorThreadControlCommandQueuePtr; CubicSDR-0.2.3/src/demod/DemodulatorInstance.cpp000066400000000000000000000460601322677621400214610ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include #include #include "DemodulatorInstance.h" #include "CubicSDR.h" #include "DemodulatorThread.h" #include "DemodulatorPreThread.h" #include "AudioSinkFileThread.h" #include "AudioFileWAV.h" #if USE_HAMLIB #include "RigThread.h" #endif DemodVisualCue::DemodVisualCue() { squelchBreak.store(false); } DemodVisualCue::~DemodVisualCue() { } void DemodVisualCue::triggerSquelchBreak(int counter) { squelchBreak.store(counter); } int DemodVisualCue::getSquelchBreak() { return squelchBreak.load(); } void DemodVisualCue::step() { if (squelchBreak.load()) { squelchBreak--; if (squelchBreak.load() < 0) { squelchBreak.store(0); } } } DemodulatorInstance::DemodulatorInstance() { #if ENABLE_DIGITAL_LAB activeOutput = nullptr; #endif active.store(false); squelch.store(false); muted.store(false); recording.store(false); deltaLock.store(false); deltaLockOfs.store(0); currentOutputDevice.store(-1); currentAudioGain.store(1.0); follow.store(false); tracking.store(false); label.store(new std::string("Unnamed")); user_label.store(new std::wstring()); pipeIQInputData = std::make_shared(); pipeIQInputData->set_max_num_items(100); pipeIQDemodData = std::make_shared< DemodulatorThreadPostInputQueue>(); pipeIQInputData->set_max_num_items(100); audioThread = new AudioThread(); demodulatorPreThread = new DemodulatorPreThread(this); demodulatorPreThread->setInputQueue("IQDataInput",pipeIQInputData); demodulatorPreThread->setOutputQueue("IQDataOutput",pipeIQDemodData); pipeAudioData = std::make_shared(); pipeAudioData->set_max_num_items(10); threadQueueControl = std::make_shared(); threadQueueControl->set_max_num_items(2); demodulatorThread = new DemodulatorThread(this); demodulatorThread->setInputQueue("IQDataInput",pipeIQDemodData); demodulatorThread->setInputQueue("ControlQueue",threadQueueControl); demodulatorThread->setOutputQueue("AudioDataOutput", pipeAudioData); audioThread->setInputQueue("AudioDataInput", pipeAudioData); } DemodulatorInstance::~DemodulatorInstance() { std::lock_guard < std::recursive_mutex > lockData(m_thread_control_mutex); //now that DemodulatorInstance are managed through shared_ptr, we //should enter here ONLY when it is no longer used by any piece of code, anywhere. //so active wait on IsTerminated(), then die. #define TERMINATION_SPIN_WAIT_MS (20) #define MAX_WAIT_FOR_TERMINATION_MS (3000.0) //this is a stupid busy plus sleep loop int nbCyclesToWait = (MAX_WAIT_FOR_TERMINATION_MS / TERMINATION_SPIN_WAIT_MS) + 1; int currentCycle = 0; while (currentCycle < nbCyclesToWait) { if (isTerminated()) { std::cout << "Garbage collected demodulator instance '" << getLabel() << "'... " << std::endl << std::flush; #if ENABLE_DIGITAL_LAB delete activeOutput; #endif delete demodulatorPreThread; delete demodulatorThread; delete audioThread; delete audioSinkThread; break; } else { std::this_thread::sleep_for(std::chrono::milliseconds(TERMINATION_SPIN_WAIT_MS)); } currentCycle++; } //end while } void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueuePtr tQueue) { demodulatorThread->setOutputQueue("AudioVisualOutput", tQueue); } void DemodulatorInstance::run() { std::lock_guard < std::recursive_mutex > lockData(m_thread_control_mutex); if (active) { return; } t_Audio = new std::thread(&AudioThread::threadMain, audioThread); #ifdef __APPLE__ // Already using pthreads, might as well do some custom init.. pthread_attr_t attr; size_t size; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 2048000); pthread_attr_getstacksize(&attr, &size); pthread_create(&t_PreDemod, &attr, &DemodulatorPreThread::pthread_helper, demodulatorPreThread); pthread_attr_destroy(&attr); pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 2048000); pthread_attr_getstacksize(&attr, &size); pthread_create(&t_Demod, &attr, &DemodulatorThread::pthread_helper, demodulatorThread); pthread_attr_destroy(&attr); // std::cout << "Initialized demodulator stack size of " << size << std::endl; #else t_PreDemod = new std::thread(&DemodulatorPreThread::threadMain, demodulatorPreThread); t_Demod = new std::thread(&DemodulatorThread::threadMain, demodulatorThread); #endif active = true; } void DemodulatorInstance::updateLabel(long long freq) { std::stringstream newLabel; newLabel.precision(3); newLabel << std::fixed << ((long double) freq / 1000000.0); setLabel(newLabel.str()); wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::terminate() { #if ENABLE_DIGITAL_LAB if (activeOutput) { closeOutput(); } #endif // std::cout << "Terminating demodulator audio thread.." << std::endl; audioThread->terminate(); // std::cout << "Terminating demodulator thread.." << std::endl; demodulatorThread->terminate(); // std::cout << "Terminating demodulator preprocessor thread.." << std::endl; demodulatorPreThread->terminate(); if (audioSinkThread != nullptr) { stopRecording(); } //that will actually unblock the currently blocked push(). pipeIQInputData->flush(); pipeAudioData->flush(); pipeIQDemodData->flush(); threadQueueControl->flush(); } std::string DemodulatorInstance::getLabel() { return *(label.load()); } void DemodulatorInstance::setLabel(std::string labelStr) { delete label.exchange(new std::string(labelStr)); } bool DemodulatorInstance::isTerminated() { std::lock_guard < std::recursive_mutex > lockData(m_thread_control_mutex); bool audioTerminated = audioThread->isTerminated(); bool demodTerminated = demodulatorThread->isTerminated(); bool preDemodTerminated = demodulatorPreThread->isTerminated(); bool audioSinkTerminated = (audioSinkThread == nullptr) || audioSinkThread->isTerminated(); //Cleanup the worker threads, if the threads are indeed terminated. // threads are linked as t_PreDemod ==> t_Demod ==> t_Audio //so terminate in the same order to starve the following threads in succession. //i.e waiting on timed-pop so able to se their stopping flag. if (preDemodTerminated) { if (t_PreDemod) { #ifdef __APPLE__ pthread_join(t_PreDemod, NULL); #else t_PreDemod->join(); delete t_PreDemod; #endif t_PreDemod = nullptr; } } if (demodTerminated) { if (t_Demod) { #ifdef __APPLE__ pthread_join(t_Demod, nullptr); #else t_Demod->join(); delete t_Demod; #endif t_Demod = nullptr; } } if (audioTerminated) { if (t_Audio) { #ifdef __APPLE__ pthread_join(t_PreDemod, NULL); #else t_Audio->join(); delete t_Audio; #endif t_Audio = nullptr; } } if (audioSinkTerminated) { if (t_AudioSink != nullptr) { t_AudioSink->join(); delete t_AudioSink; t_AudioSink = nullptr; } } bool terminated = audioTerminated && demodTerminated && preDemodTerminated && audioSinkTerminated; return terminated; } bool DemodulatorInstance::isActive() { return active; } void DemodulatorInstance::setActive(bool state) { if (active && !state) { #if ENABLE_DIGITAL_LAB if (activeOutput) { activeOutput->Hide(); } #endif audioThread->setActive(state); DemodulatorThread::releaseSquelchLock(this); } else if (!active && state) { #if ENABLE_DIGITAL_LAB if (activeOutput && getModemType() == "digital") { activeOutput->Show(); } #endif audioThread->setActive(state); } if (!state) { tracking = false; } active = state; wxGetApp().getBookmarkMgr().updateActiveList(); } void DemodulatorInstance::squelchAuto() { DemodulatorThreadControlCommand command; command.cmd = DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_ON; //VSO: blocking push threadQueueControl->push(command); squelch = true; } bool DemodulatorInstance::isSquelchEnabled() { return (demodulatorThread->getSquelchLevel() != 0.0); } void DemodulatorInstance::setSquelchEnabled(bool state) { if (!state && squelch) { DemodulatorThreadControlCommand command; command.cmd = DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_OFF; threadQueueControl->push(command); } else if (state && !squelch) { DemodulatorThreadControlCommand command; command.cmd = DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_ON; //VSO: blocking push! threadQueueControl->push(command); } squelch = state; } float DemodulatorInstance::getSignalLevel() { return demodulatorThread->getSignalLevel(); } float DemodulatorInstance::getSignalFloor() { return demodulatorThread->getSignalFloor(); } float DemodulatorInstance::getSignalCeil() { return demodulatorThread->getSignalCeil(); } void DemodulatorInstance::setSquelchLevel(float signal_level_in) { demodulatorThread->setSquelchLevel(signal_level_in); wxGetApp().getDemodMgr().setLastSquelchLevel(signal_level_in); wxGetApp().getDemodMgr().setLastSquelchEnabled(true); } float DemodulatorInstance::getSquelchLevel() { return demodulatorThread->getSquelchLevel(); } void DemodulatorInstance::setOutputDevice(int device_id) { if (!active) { audioThread->setInitOutputDevice(device_id); } else if (audioThread) { AudioThreadCommand command; command.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE; command.int_value = device_id; //VSO: blocking push audioThread->getCommandQueue()->push(command); } setAudioSampleRate(AudioThread::deviceSampleRate[device_id]); currentOutputDevice = device_id; } int DemodulatorInstance::getOutputDevice() { if (currentOutputDevice == -1) { if (audioThread) { currentOutputDevice = audioThread->getOutputDevice(); } } return currentOutputDevice; } void DemodulatorInstance::setDemodulatorType(std::string demod_type_in) { setGain(getGain()); if (demodulatorPreThread) { std::string currentDemodType = demodulatorPreThread->getDemodType(); if ((currentDemodType != "") && (currentDemodType != demod_type_in)) { lastModemSettings[currentDemodType] = demodulatorPreThread->readModemSettings(); lastModemBandwidth[currentDemodType] = demodulatorPreThread->getBandwidth(); } #if ENABLE_DIGITAL_LAB if (activeOutput) { activeOutput->Hide(); } #endif demodulatorPreThread->setDemodType(demod_type_in); int lastbw = 0; if (currentDemodType != "" && lastModemBandwidth.find(demod_type_in) != lastModemBandwidth.end()) { lastbw = lastModemBandwidth[demod_type_in]; } if (!lastbw) { lastbw = Modem::getModemDefaultSampleRate(demod_type_in); } if (lastbw) { setBandwidth(lastbw); } #if ENABLE_DIGITAL_LAB if (isModemInitialized() && getModemType() == "digital") { ModemDigitalOutputConsole *outp = (ModemDigitalOutputConsole *)getOutput(); outp->setTitle(getDemodulatorType() + ": " + frequencyToStr(getFrequency())); } #endif } wxGetApp().getBookmarkMgr().updateActiveList(); } std::string DemodulatorInstance::getDemodulatorType() { return demodulatorPreThread->getDemodType(); } std::wstring DemodulatorInstance::getDemodulatorUserLabel() { return *(user_label.load()); } void DemodulatorInstance::setDemodulatorUserLabel(const std::wstring& demod_user_label) { delete user_label.exchange(new std::wstring(demod_user_label)); } void DemodulatorInstance::setDemodulatorLock(bool demod_lock_in) { Modem *cModem = demodulatorPreThread->getModem(); if (cModem && cModem->getType() == "digital") { ((ModemDigital *)cModem)->setDemodulatorLock(demod_lock_in); } } int DemodulatorInstance::getDemodulatorLock() { Modem *cModem = demodulatorPreThread->getModem(); if (cModem && cModem->getType() == "digital") { return ((ModemDigital *)cModem)->getDemodulatorLock(); } return 0; } void DemodulatorInstance::setBandwidth(int bw) { demodulatorPreThread->setBandwidth(bw); } int DemodulatorInstance::getBandwidth() { return demodulatorPreThread->getBandwidth(); } void DemodulatorInstance::setFrequency(long long freq) { if ((freq - getBandwidth() / 2) <= 0) { freq = getBandwidth() / 2; } demodulatorPreThread->setFrequency(freq); #if ENABLE_DIGITAL_LAB if (activeOutput) { if (isModemInitialized() && getModemType() == "digital") { ModemDigitalOutputConsole *outp = (ModemDigitalOutputConsole *)getOutput(); outp->setTitle(getDemodulatorType() + ": " + frequencyToStr(getFrequency())); } } #endif #if USE_HAMLIB if (wxGetApp().rigIsActive() && wxGetApp().getRigThread()->getFollowModem() && wxGetApp().getDemodMgr().getLastActiveDemodulator().get() == this) { wxGetApp().getRigThread()->setFrequency(freq,true); } #endif if (this->isActive()) { wxGetApp().getBookmarkMgr().updateActiveList(); } } long long DemodulatorInstance::getFrequency() { return demodulatorPreThread->getFrequency(); } void DemodulatorInstance::setAudioSampleRate(int sampleRate) { demodulatorPreThread->setSampleRate(sampleRate); } int DemodulatorInstance::getAudioSampleRate() { if (!audioThread) { return 0; } return audioThread->getSampleRate(); } void DemodulatorInstance::setGain(float gain_in) { currentAudioGain = gain_in; audioThread->setGain(gain_in); } float DemodulatorInstance::getGain() { return currentAudioGain; } bool DemodulatorInstance::isFollow() { return follow.load(); } void DemodulatorInstance::setFollow(bool follow) { this->follow.store(follow); } bool DemodulatorInstance::isTracking() { return tracking.load(); } void DemodulatorInstance::setTracking(bool tracking) { this->tracking.store(tracking); } bool DemodulatorInstance::isDeltaLock() { return deltaLock.load(); } void DemodulatorInstance::setDeltaLock(bool lock) { deltaLock.store(lock); } void DemodulatorInstance::setDeltaLockOfs(int lockOfs) { deltaLockOfs.store(lockOfs); } int DemodulatorInstance::getDeltaLockOfs() { return deltaLockOfs.load(); } bool DemodulatorInstance::isMuted() { return demodulatorThread->isMuted(); } void DemodulatorInstance::setMuted(bool muted) { this->muted = muted; demodulatorThread->setMuted(muted); wxGetApp().getDemodMgr().setLastMuted(muted); } bool DemodulatorInstance::isRecording() { return recording.load(); } void DemodulatorInstance::setRecording(bool recording_in) { if (!recording.load() && recording_in) { startRecording(); } else if (recording.load() && !recording_in) { stopRecording(); } } DemodVisualCue *DemodulatorInstance::getVisualCue() { return &visualCue; } DemodulatorThreadInputQueuePtr DemodulatorInstance::getIQInputDataPipe() { return pipeIQInputData; } ModemArgInfoList DemodulatorInstance::getModemArgs() { Modem *m = demodulatorPreThread->getModem(); ModemArgInfoList args; if (m != nullptr) { args = m->getSettings(); } return args; } std::string DemodulatorInstance::readModemSetting(std::string setting) { return demodulatorPreThread->readModemSetting(setting); } void DemodulatorInstance::writeModemSetting(std::string setting, std::string value) { demodulatorPreThread->writeModemSetting(setting, value); } ModemSettings DemodulatorInstance::readModemSettings() { return demodulatorPreThread->readModemSettings(); } void DemodulatorInstance::writeModemSettings(ModemSettings settings) { demodulatorPreThread->writeModemSettings(settings); } bool DemodulatorInstance::isModemInitialized() { if (!demodulatorPreThread || isTerminated()) { return false; } return demodulatorPreThread->isInitialized(); } std::string DemodulatorInstance::getModemType() { if (isModemInitialized()) { return demodulatorPreThread->getModem()->getType(); } return ""; } ModemSettings DemodulatorInstance::getLastModemSettings(std::string demodType) { if (lastModemSettings.find(demodType) != lastModemSettings.end()) { return lastModemSettings[demodType]; } else { ModemSettings mods; return mods; } } void DemodulatorInstance::startRecording() { if (recording.load()) { return; } AudioSinkFileThread *newSinkThread = new AudioSinkFileThread(); AudioFileWAV *afHandler = new AudioFileWAV(); std::stringstream fileName; std::wstring userLabel = getDemodulatorUserLabel(); wxString userLabelForFileName(userLabel); std::string userLabelStr = userLabelForFileName.ToStdString(); if (!userLabelStr.empty()) { fileName << userLabelStr; } else { fileName << getLabel(); } newSinkThread->setAudioFileNameBase(fileName.str()); //attach options: newSinkThread->setSquelchOption(wxGetApp().getConfig()->getRecordingSquelchOption()); newSinkThread->setFileTimeLimit(wxGetApp().getConfig()->getRecordingFileTimeLimit()); newSinkThread->setAudioFileHandler(afHandler); audioSinkThread = newSinkThread; t_AudioSink = new std::thread(&AudioSinkThread::threadMain, audioSinkThread); demodulatorThread->setOutputQueue("AudioSink", audioSinkThread->getInputQueue("input")); recording.store(true); } void DemodulatorInstance::stopRecording() { if (!recording.load()) { return; } demodulatorThread->setOutputQueue("AudioSink", nullptr); audioSinkThread->terminate(); t_AudioSink->join(); delete t_AudioSink; delete audioSinkThread; t_AudioSink = nullptr; audioSinkThread = nullptr; recording.store(false); } #if ENABLE_DIGITAL_LAB ModemDigitalOutput *DemodulatorInstance::getOutput() { if (activeOutput == nullptr) { activeOutput = new ModemDigitalOutputConsole(); } return activeOutput; } void DemodulatorInstance::showOutput() { if (activeOutput != nullptr) { activeOutput->Show(); } } void DemodulatorInstance::hideOutput() { if (activeOutput != nullptr) { activeOutput->Hide(); } } void DemodulatorInstance::closeOutput() { if (isModemInitialized()) { if (getModemType() == "digital") { ModemDigital *dModem = (ModemDigital *)demodulatorPreThread->getModem(); dModem->setOutput(nullptr); } } if (activeOutput) { activeOutput->Close(); } } #endif CubicSDR-0.2.3/src/demod/DemodulatorInstance.h000066400000000000000000000105431322677621400211230ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include "DemodDefs.h" #include "ModemDigital.h" #include "ModemAnalog.h" #include "AudioThread.h" #include "AudioSinkThread.h" #if ENABLE_DIGITAL_LAB #include "DigitalConsole.h" #endif class DemodVisualCue { public: DemodVisualCue(); ~DemodVisualCue(); void triggerSquelchBreak(int counter); int getSquelchBreak(); void step(); private: std::atomic_int squelchBreak; }; class DemodulatorThread; class DemodulatorPreThread; class DemodulatorInstance { public: #ifdef __APPLE__ pthread_t t_PreDemod; pthread_t t_Demod; #else std::thread *t_PreDemod = nullptr; std::thread *t_Demod = nullptr; #endif AudioThread *audioThread = nullptr; std::thread *t_Audio = nullptr; DemodulatorInstance(); ~DemodulatorInstance(); void setVisualOutputQueue(DemodulatorThreadOutputQueuePtr tQueue); void run(); void terminate(); std::string getLabel(); void setLabel(std::string labelStr); bool isTerminated(); void updateLabel(long long freq); bool isActive(); void setActive(bool state); void squelchAuto(); bool isSquelchEnabled(); void setSquelchEnabled(bool state); float getSignalLevel(); float getSignalFloor(); float getSignalCeil(); void setSquelchLevel(float signal_level_in); float getSquelchLevel(); void setOutputDevice(int device_id); int getOutputDevice(); void setDemodulatorType(std::string demod_type_in); std::string getDemodulatorType(); std::wstring getDemodulatorUserLabel(); void setDemodulatorUserLabel(const std::wstring& demod_user_label); void setDemodulatorLock(bool demod_lock_in); int getDemodulatorLock(); void setBandwidth(int bw); int getBandwidth(); void setGain(float gain_in); float getGain(); void setFrequency(long long freq); long long getFrequency(); void setAudioSampleRate(int sampleRate); int getAudioSampleRate(); bool isFollow(); void setFollow(bool follow); bool isTracking(); void setTracking(bool tracking); bool isDeltaLock(); void setDeltaLock(bool lock); void setDeltaLockOfs(int lockOfs); int getDeltaLockOfs(); bool isMuted(); void setMuted(bool muted); bool isRecording(); void setRecording(bool recording); DemodVisualCue *getVisualCue(); DemodulatorThreadInputQueuePtr getIQInputDataPipe(); ModemArgInfoList getModemArgs(); std::string readModemSetting(std::string setting); ModemSettings readModemSettings(); void writeModemSetting(std::string setting, std::string value); void writeModemSettings(ModemSettings settings); bool isModemInitialized(); std::string getModemType(); ModemSettings getLastModemSettings(std::string demodType); #if ENABLE_DIGITAL_LAB ModemDigitalOutput *getOutput(); void showOutput(); void hideOutput(); void closeOutput(); #endif protected: void startRecording(); void stopRecording(); private: DemodulatorThreadInputQueuePtr pipeIQInputData; DemodulatorThreadPostInputQueuePtr pipeIQDemodData; AudioThreadInputQueuePtr pipeAudioData; DemodulatorPreThread *demodulatorPreThread; DemodulatorThread *demodulatorThread; DemodulatorThreadControlCommandQueuePtr threadQueueControl; AudioSinkThread *audioSinkThread = nullptr; std::thread *t_AudioSink = nullptr; AudioThreadInputQueuePtr audioSinkInputQueue; //protects child thread creation and termination std::recursive_mutex m_thread_control_mutex; std::atomic label; // // User editable buffer, 16 bit string. std::atomic user_label; std::atomic_bool active; std::atomic_bool squelch; std::atomic_bool muted; std::atomic_bool deltaLock; std::atomic_bool recording; std::atomic_int deltaLockOfs; std::atomic_int currentOutputDevice; std::atomic currentAudioGain; std::atomic_bool follow, tracking; std::map lastModemSettings; std::map lastModemBandwidth; DemodVisualCue visualCue; #if ENABLE_DIGITAL_LAB ModemDigitalOutput *activeOutput; #endif }; typedef std::shared_ptr DemodulatorInstancePtr; CubicSDR-0.2.3/src/demod/DemodulatorMgr.cpp000066400000000000000000000422641322677621400204440ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include #include #include #include #include #include #include "DemodulatorMgr.h" #include "CubicSDR.h" #if USE_HAMLIB #include "RigThread.h" #endif #include "DataTree.h" bool demodFreqCompare (DemodulatorInstancePtr i, DemodulatorInstancePtr j) { return (i->getFrequency() < j->getFrequency()); } bool inactiveCompare (DemodulatorInstancePtr i, DemodulatorInstancePtr j) { return (i->isActive() < j->isActive()); } DemodulatorMgr::DemodulatorMgr() { lastBandwidth = DEFAULT_DEMOD_BW; lastDemodType = DEFAULT_DEMOD_TYPE; lastSquelchEnabled = false; lastSquelch = -100; lastGain = 1.0; lastMuted = false; lastDeltaLock = false; } DemodulatorMgr::~DemodulatorMgr() { terminateAll(); } DemodulatorInstancePtr DemodulatorMgr::newThread() { std::lock_guard < std::recursive_mutex > lock(demods_busy); //create a new instance of DemodulatorInstance here. DemodulatorInstancePtr newDemod = std::make_shared(); std::stringstream label; label << demods.size(); newDemod->setLabel(label.str()); demods.push_back(newDemod); return newDemod; } void DemodulatorMgr::terminateAll() { std::lock_guard < std::recursive_mutex > lock(demods_busy); while (demods.size()) { DemodulatorInstancePtr d = demods.back(); demods.pop_back(); deleteThread(d); } } std::vector DemodulatorMgr::getDemodulators() { std::lock_guard < std::recursive_mutex > lock(demods_busy); return demods; } std::vector DemodulatorMgr::getOrderedDemodulators(bool actives) { std::lock_guard < std::recursive_mutex > lock(demods_busy); auto demods_ordered = demods; if (actives) { std::sort(demods_ordered.begin(), demods_ordered.end(), inactiveCompare); std::vector::iterator i; for (i = demods_ordered.begin(); i != demods_ordered.end(); i++) { if ((*i)->isActive()) { break; } } if (i == demods_ordered.end()) { demods_ordered.erase(demods_ordered.begin(), demods_ordered.end()); } else if ((*i) != demods_ordered.front()) { demods_ordered.erase(demods_ordered.begin(), i); } } //if by chance they have the same frequency, keep their relative order std::stable_sort(demods_ordered.begin(), demods_ordered.end(), demodFreqCompare); return demods_ordered; } DemodulatorInstancePtr DemodulatorMgr::getPreviousDemodulator(DemodulatorInstancePtr demod, bool actives) { std::lock_guard < std::recursive_mutex > lock(demods_busy); if (!getLastActiveDemodulator()) { return nullptr; } auto demods_ordered = getOrderedDemodulators(actives); auto p = std::find(demods_ordered.begin(), demods_ordered.end(), demod); if (p == demods_ordered.end()) { return nullptr; } if (*p == demods_ordered.front()) { return demods_ordered.back(); } return *(--p); } DemodulatorInstancePtr DemodulatorMgr::getNextDemodulator(DemodulatorInstancePtr demod, bool actives) { std::lock_guard < std::recursive_mutex > lock(demods_busy); if (!getLastActiveDemodulator()) { return nullptr; } auto demods_ordered = getOrderedDemodulators(actives); auto p = std::find(demods_ordered.begin(), demods_ordered.end(), demod); if (actives) { } if (p == demods_ordered.end()) { return nullptr; } if (*p == demods_ordered.back()) { return demods_ordered.front(); } return *(++p); } DemodulatorInstancePtr DemodulatorMgr::getLastDemodulator() { std::lock_guard < std::recursive_mutex > lock(demods_busy); return getOrderedDemodulators().back(); } DemodulatorInstancePtr DemodulatorMgr::getFirstDemodulator() { std::lock_guard < std::recursive_mutex > lock(demods_busy); return getOrderedDemodulators().front(); } void DemodulatorMgr::deleteThread(DemodulatorInstancePtr demod) { std::lock_guard < std::recursive_mutex > lock(demods_busy); wxGetApp().getBookmarkMgr().addRecent(demod); auto i = std::find(demods.begin(), demods.end(), demod); if (activeDemodulator == demod) { activeDemodulator = nullptr; } if (lastActiveDemodulator == demod) { lastActiveDemodulator = nullptr; } if (activeVisualDemodulator == demod) { activeVisualDemodulator = nullptr; } if (i != demods.end()) { demods.erase(i); } //Ask for termination demod->setActive(false); demod->terminate(); } std::vector DemodulatorMgr::getDemodulatorsAt(long long freq, int bandwidth) { std::lock_guard < std::recursive_mutex > lock(demods_busy); std::vector foundDemods; for (int i = 0, iMax = demods.size(); i < iMax; i++) { DemodulatorInstancePtr testDemod = demods[i]; long long freqTest = testDemod->getFrequency(); long long bandwidthTest = testDemod->getBandwidth(); long long halfBandwidthTest = bandwidthTest / 2; long long halfBuffer = bandwidth / 2; if ((freq <= (freqTest + ((testDemod->getDemodulatorType() != "LSB")?halfBandwidthTest:0) + halfBuffer)) && (freq >= (freqTest - ((testDemod->getDemodulatorType() != "USB")?halfBandwidthTest:0) - halfBuffer))) { foundDemods.push_back(testDemod); } } return foundDemods; } bool DemodulatorMgr::anyDemodulatorsAt(long long freq, int bandwidth) { std::lock_guard < std::recursive_mutex > lock(demods_busy); for (int i = 0, iMax = demods.size(); i < iMax; i++) { DemodulatorInstancePtr testDemod = demods[i]; long long freqTest = testDemod->getFrequency(); long long bandwidthTest = testDemod->getBandwidth(); long long halfBandwidthTest = bandwidthTest / 2; long long halfBuffer = bandwidth / 2; if ((freq <= (freqTest + ((testDemod->getDemodulatorType() != "LSB")?halfBandwidthTest:0) + halfBuffer)) && (freq >= (freqTest - ((testDemod->getDemodulatorType() != "USB")?halfBandwidthTest:0) - halfBuffer))) { return true; } } return false; } void DemodulatorMgr::setActiveDemodulator(DemodulatorInstancePtr demod, bool temporary) { std::lock_guard < std::recursive_mutex > lock(demods_busy); if (!temporary) { if (activeDemodulator != nullptr) { lastActiveDemodulator = activeDemodulator; updateLastState(); } else { lastActiveDemodulator = demod; } updateLastState(); #if USE_HAMLIB if (wxGetApp().rigIsActive() && wxGetApp().getRigThread()->getFollowModem() && lastActiveDemodulator) { wxGetApp().getRigThread()->setFrequency(lastActiveDemodulator->getFrequency(),true); } #endif wxGetApp().getBookmarkMgr().updateActiveList(); } if (activeVisualDemodulator) { activeVisualDemodulator->setVisualOutputQueue(nullptr); } if (demod) { demod->setVisualOutputQueue(wxGetApp().getAudioVisualQueue()); activeVisualDemodulator = demod; } else { DemodulatorInstancePtr last = getLastActiveDemodulator(); if (last) { last->setVisualOutputQueue(wxGetApp().getAudioVisualQueue()); } activeVisualDemodulator = last; } activeDemodulator = demod; } //Dangerous: this is only intended by some internal classes void DemodulatorMgr::setActiveDemodulatorByRawPointer(DemodulatorInstance* demod, bool temporary) { std::lock_guard < std::recursive_mutex > lock(demods_busy); for (auto existing_demod : demods) { if (existing_demod.get() == demod) { setActiveDemodulator(existing_demod, temporary); break; } } } DemodulatorInstancePtr DemodulatorMgr::getActiveDemodulator() { std::lock_guard < std::recursive_mutex > lock(demods_busy); if (activeDemodulator && !activeDemodulator->isActive()) { activeDemodulator = getLastActiveDemodulator(); } return activeDemodulator; } DemodulatorInstancePtr DemodulatorMgr::getLastActiveDemodulator() { return lastActiveDemodulator; } DemodulatorInstancePtr DemodulatorMgr::getLastDemodulatorWith(const std::string& type, const std::wstring& userLabel, long long frequency, int bandwidth) { std::lock_guard < std::recursive_mutex > lock(demods_busy); //backwards search: for (auto it = demods.rbegin(); it != demods.rend(); it++) { if ((*it)->getDemodulatorType() == type && (*it)->getDemodulatorUserLabel() == userLabel && (*it)->getFrequency() == frequency && (*it)->getBandwidth() == bandwidth) { return (*it); } } return nullptr; } void DemodulatorMgr::updateLastState() { std::lock_guard < std::recursive_mutex > lock(demods_busy); if (std::find(demods.begin(), demods.end(), lastActiveDemodulator) == demods.end()) { if (activeDemodulator && activeDemodulator->isActive()) { lastActiveDemodulator = activeDemodulator; } else if (activeDemodulator && !activeDemodulator->isActive()){ activeDemodulator = nullptr; lastActiveDemodulator = nullptr; } } if (lastActiveDemodulator && !lastActiveDemodulator->isActive()) { lastActiveDemodulator = nullptr; } if (lastActiveDemodulator) { lastBandwidth = lastActiveDemodulator->getBandwidth(); lastDemodType = lastActiveDemodulator->getDemodulatorType(); lastDemodLock = lastActiveDemodulator->getDemodulatorLock()?true:false; lastSquelchEnabled = lastActiveDemodulator->isSquelchEnabled(); lastSquelch = lastActiveDemodulator->getSquelchLevel(); lastGain = lastActiveDemodulator->getGain(); lastModemSettings[lastDemodType] = lastActiveDemodulator->readModemSettings(); } } int DemodulatorMgr::getLastBandwidth() const { return lastBandwidth; } void DemodulatorMgr::setLastBandwidth(int lastBandwidth) { if (lastBandwidth < MIN_BANDWIDTH) { lastBandwidth = MIN_BANDWIDTH; } else if (lastBandwidth > wxGetApp().getSampleRate()) { lastBandwidth = wxGetApp().getSampleRate(); } this->lastBandwidth = lastBandwidth; } std::string DemodulatorMgr::getLastDemodulatorType() const { return lastDemodType; } void DemodulatorMgr::setLastDemodulatorType(std::string lastDemodType) { this->lastDemodType = lastDemodType; } float DemodulatorMgr::getLastGain() const { return lastGain; } void DemodulatorMgr::setLastGain(float lastGain) { this->lastGain = lastGain; } bool DemodulatorMgr::getLastDeltaLock() const { return lastDeltaLock; } void DemodulatorMgr::setLastDeltaLock(bool lock) { lastDeltaLock = lock; } float DemodulatorMgr::getLastSquelchLevel() const { return lastSquelch; } void DemodulatorMgr::setLastSquelchLevel(float lastSquelch) { this->lastSquelch = lastSquelch; } bool DemodulatorMgr::isLastSquelchEnabled() const { return lastSquelchEnabled; } void DemodulatorMgr::setLastSquelchEnabled(bool lastSquelchEnabled) { this->lastSquelchEnabled = lastSquelchEnabled; } bool DemodulatorMgr::isLastMuted() const { return lastMuted; } void DemodulatorMgr::setLastMuted(bool lastMuted) { this->lastMuted = lastMuted; } ModemSettings DemodulatorMgr::getLastModemSettings(std::string modemType) { return lastModemSettings[modemType]; } void DemodulatorMgr::setLastModemSettings(std::string modemType, ModemSettings settings) { lastModemSettings[modemType] = settings; } void DemodulatorMgr::setOutputDevices(std::map devs) { outputDevices = devs; } void DemodulatorMgr::saveInstance(DataNode *node, DemodulatorInstancePtr inst) { *node->newChild("bandwidth") = inst->getBandwidth(); *node->newChild("frequency") = inst->getFrequency(); *node->newChild("type") = inst->getDemodulatorType(); node->newChild("user_label")->element()->set(inst->getDemodulatorUserLabel()); *node->newChild("squelch_level") = inst->getSquelchLevel(); *node->newChild("squelch_enabled") = inst->isSquelchEnabled() ? 1 : 0; *node->newChild("output_device") = outputDevices[inst->getOutputDevice()].name; *node->newChild("gain") = inst->getGain(); *node->newChild("muted") = inst->isMuted() ? 1 : 0; if (inst->isDeltaLock()) { *node->newChild("delta_lock") = inst->isDeltaLock() ? 1 : 0; *node->newChild("delta_ofs") = inst->getDeltaLockOfs(); } if (inst == getLastActiveDemodulator()) { *node->newChild("active") = 1; } ModemSettings saveSettings = inst->readModemSettings(); if (saveSettings.size()) { DataNode *settingsNode = node->newChild("settings"); for (ModemSettings::const_iterator msi = saveSettings.begin(); msi != saveSettings.end(); msi++) { *settingsNode->newChild(msi->first.c_str()) = msi->second; } } } DemodulatorInstancePtr DemodulatorMgr::loadInstance(DataNode *node) { std::lock_guard < std::recursive_mutex > lock(demods_busy); DemodulatorInstancePtr newDemod = nullptr; node->rewindAll(); long bandwidth = *node->getNext("bandwidth"); long long freq = *node->getNext("frequency"); float squelch_level = node->hasAnother("squelch_level") ? (float) *node->getNext("squelch_level") : 0; int squelch_enabled = node->hasAnother("squelch_enabled") ? (int) *node->getNext("squelch_enabled") : 0; int muted = node->hasAnother("muted") ? (int) *node->getNext("muted") : 0; int delta_locked = node->hasAnother("delta_lock") ? (int) *node->getNext("delta_lock") : 0; int delta_ofs = node->hasAnother("delta_ofs") ? (int) *node->getNext("delta_ofs") : 0; std::string output_device = node->hasAnother("output_device") ? string(*(node->getNext("output_device"))) : ""; float gain = node->hasAnother("gain") ? (float) *node->getNext("gain") : 1.0; std::string type = "FM"; DataNode *demodTypeNode = node->hasAnother("type")?node->getNext("type"):nullptr; if (demodTypeNode && demodTypeNode->element()->getDataType() == DATA_INT) { int legacyType = *demodTypeNode; int legacyStereo = node->hasAnother("stereo") ? (int) *node->getNext("stereo") : 0; switch (legacyType) { // legacy demod ID case 1: type = legacyStereo?"FMS":"FM"; break; case 2: type = "AM"; break; case 3: type = "LSB"; break; case 4: type = "USB"; break; case 5: type = "DSB"; break; case 6: type = "ASK"; break; case 7: type = "APSK"; break; case 8: type = "BPSK"; break; case 9: type = "DPSK"; break; case 10: type = "PSK"; break; case 11: type = "OOK"; break; case 12: type = "ST"; break; case 13: type = "SQAM"; break; case 14: type = "QAM"; break; case 15: type = "QPSK"; break; case 16: type = "I/Q"; break; default: type = "FM"; break; } } else if (demodTypeNode && demodTypeNode->element()->getDataType() == DATA_STRING) { demodTypeNode->element()->get(type); } //read the user label associated with the demodulator std::wstring user_label = L""; DataNode *demodUserLabel = node->hasAnother("user_label") ? node->getNext("user_label") : nullptr; if (demodUserLabel) { demodUserLabel->element()->get(user_label); } ModemSettings mSettings; if (node->hasAnother("settings")) { DataNode *modemSettings = node->getNext("settings"); for (int msi = 0, numSettings = modemSettings->numChildren(); msi < numSettings; msi++) { DataNode *settingNode = modemSettings->child(msi); std::string keyName = settingNode->getName(); std::string strSettingValue = settingNode->element()->toString(); if (keyName != "" && strSettingValue != "") { mSettings[keyName] = strSettingValue; } } } newDemod = newThread(); newDemod->setDemodulatorType(type); newDemod->setDemodulatorUserLabel(user_label); newDemod->writeModemSettings(mSettings); newDemod->setBandwidth(bandwidth); newDemod->setFrequency(freq); newDemod->setGain(gain); newDemod->updateLabel(freq); newDemod->setMuted(muted?true:false); if (delta_locked) { newDemod->setDeltaLock(true); newDemod->setDeltaLockOfs(delta_ofs); } if (squelch_enabled) { newDemod->setSquelchEnabled(true); newDemod->setSquelchLevel(squelch_level); } //Attach to sound output: std::map::iterator i; bool matching_device_found = false; for (i = outputDevices.begin(); i != outputDevices.end(); i++) { if (i->second.name == output_device) { newDemod->setOutputDevice(i->first); matching_device_found = true; break; } } //if no device is found, choose the first of the list anyway. if (!matching_device_found) { newDemod->setOutputDevice(outputDevices.begin()->first); } return newDemod; } CubicSDR-0.2.3/src/demod/DemodulatorMgr.h000066400000000000000000000062451322677621400201100ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include "DemodulatorInstance.h" class DataNode; class DemodulatorMgr { public: DemodulatorMgr(); ~DemodulatorMgr(); DemodulatorInstancePtr newThread(); //return snapshot-copy of the list purposefully std::vector getDemodulators(); std::vector getOrderedDemodulators(bool actives = true); std::vector getDemodulatorsAt(long long freq, int bandwidth); DemodulatorInstancePtr getPreviousDemodulator(DemodulatorInstancePtr demod, bool actives = true); DemodulatorInstancePtr getNextDemodulator(DemodulatorInstancePtr demod, bool actives = true); DemodulatorInstancePtr getLastDemodulator(); DemodulatorInstancePtr getFirstDemodulator(); bool anyDemodulatorsAt(long long freq, int bandwidth); void deleteThread(DemodulatorInstancePtr); void terminateAll(); void setActiveDemodulator(DemodulatorInstancePtr demod, bool temporary = true); //Dangerous: this is only intended by some internal classes, // and only set a pre-existing demod void setActiveDemodulatorByRawPointer(DemodulatorInstance* demod, bool temporary = true); DemodulatorInstancePtr getActiveDemodulator(); DemodulatorInstancePtr getLastActiveDemodulator(); DemodulatorInstancePtr getLastDemodulatorWith(const std::string& type, const std::wstring& userLabel, long long frequency, int bandwidth); int getLastBandwidth() const; void setLastBandwidth(int lastBandwidth); std::string getLastDemodulatorType() const; void setLastDemodulatorType(std::string lastDemodType); float getLastGain() const; void setLastGain(float lastGain); bool getLastDeltaLock() const; void setLastDeltaLock(bool lock); float getLastSquelchLevel() const; void setLastSquelchLevel(float lastSquelch); bool isLastSquelchEnabled() const; void setLastSquelchEnabled(bool lastSquelchEnabled); bool isLastMuted() const; void setLastMuted(bool lastMuted); ModemSettings getLastModemSettings(std::string); void setLastModemSettings(std::string, ModemSettings); void updateLastState(); void setOutputDevices(std::map devs); void saveInstance(DataNode *node, DemodulatorInstancePtr inst); DemodulatorInstancePtr loadInstance(DataNode *node); private: std::vector demods; DemodulatorInstancePtr activeDemodulator; DemodulatorInstancePtr lastActiveDemodulator; DemodulatorInstancePtr activeVisualDemodulator; int lastBandwidth; std::string lastDemodType; bool lastDemodLock; bool lastSquelchEnabled; float lastSquelch; float lastGain; bool lastMuted; bool lastDeltaLock; //protects access to demods lists and such, need to be recursive //because of the usage of public re-entrant methods std::recursive_mutex demods_busy; std::map lastModemSettings; std::map outputDevices; }; CubicSDR-0.2.3/src/demod/DemodulatorPreThread.cpp000066400000000000000000000327621322677621400215770ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "CubicSDRDefs.h" #include #ifdef __APPLE__ #include #endif #include "DemodulatorPreThread.h" #include "CubicSDR.h" #include "DemodulatorInstance.h" //50 ms #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) DemodulatorPreThread::DemodulatorPreThread(DemodulatorInstance* parent) : IOThread(), iqResampler(NULL), iqResampleRatio(1), cModem(nullptr), cModemKit(nullptr) { initialized.store(false); this->parent = parent; freqShifter = nco_crcf_create(LIQUID_VCO); shiftFrequency = 0; workerQueue = std::make_shared(); workerQueue->set_max_num_items(2); workerResults = std::make_shared(); workerResults->set_max_num_items(100); workerThread = new DemodulatorWorkerThread(); workerThread->setInputQueue("WorkerCommandQueue",workerQueue); workerThread->setOutputQueue("WorkerResultQueue",workerResults); newSampleRate = currentSampleRate = 0; newBandwidth = currentBandwidth = 0; newAudioSampleRate = currentAudioSampleRate = 0; newFrequency = currentFrequency = 0; sampleRateChanged.store(false); frequencyChanged.store(false); bandwidthChanged.store(false); audioSampleRateChanged.store(false); modemSettingsChanged.store(false); demodTypeChanged.store(false); } bool DemodulatorPreThread::isInitialized() { return initialized.load(); } DemodulatorPreThread::~DemodulatorPreThread() { } void DemodulatorPreThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread int priority = sched_get_priority_max( SCHED_FIFO) - 1; sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif // std::cout << "Demodulator preprocessor thread started.." << std::endl; ReBuffer buffers("DemodulatorPreThreadBuffers"); iqInputQueue = std::static_pointer_cast(getInputQueue("IQDataInput")); iqOutputQueue = std::static_pointer_cast(getOutputQueue("IQDataOutput")); std::vector in_buf_data; std::vector out_buf_data; t_Worker = new std::thread(&DemodulatorWorkerThread::threadMain, workerThread); while (!stopping) { DemodulatorThreadIQDataPtr inp; if (!iqInputQueue->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) { continue; } if (frequencyChanged.load()) { currentFrequency.store(newFrequency); frequencyChanged.store(false); } if (inp->sampleRate != currentSampleRate) { newSampleRate = inp->sampleRate; if (newSampleRate) { sampleRateChanged.store(true); } } if (!newAudioSampleRate) { newAudioSampleRate = parent->getAudioSampleRate(); if (newAudioSampleRate) { audioSampleRateChanged.store(true); } } else if (parent->getAudioSampleRate() != newAudioSampleRate) { int newRate; if ((newRate = parent->getAudioSampleRate())) { newAudioSampleRate = parent->getAudioSampleRate(); audioSampleRateChanged.store(true); } } if (demodTypeChanged.load() && (newSampleRate && newAudioSampleRate && newBandwidth)) { DemodulatorWorkerThreadCommand command(DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_MAKE_DEMOD); command.frequency = newFrequency; command.sampleRate = newSampleRate; command.demodType = newDemodType; command.bandwidth = newBandwidth; command.audioSampleRate = newAudioSampleRate; demodType = newDemodType; sampleRateChanged.store(false); audioSampleRateChanged.store(false); ModemSettings lastSettings = parent->getLastModemSettings(newDemodType); if (lastSettings.size() != 0) { command.settings = lastSettings; if (modemSettingsBuffered.size()) { for (ModemSettings::const_iterator msi = modemSettingsBuffered.begin(); msi != modemSettingsBuffered.end(); msi++) { command.settings[msi->first] = msi->second; } } } else { command.settings = modemSettingsBuffered; } modemSettingsBuffered.clear(); modemSettingsChanged.store(false); //VSO: blocking push workerQueue->push(command); cModem = nullptr; cModemKit = nullptr; demodTypeChanged.store(false); initialized.store(false); } else if ( cModemKit && cModem && (bandwidthChanged.load() || sampleRateChanged.load() || audioSampleRateChanged.load() || cModem->shouldRebuildKit()) && (newSampleRate && newAudioSampleRate && newBandwidth) ) { DemodulatorWorkerThreadCommand command(DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_BUILD_FILTERS); command.frequency = newFrequency; command.sampleRate = newSampleRate; command.bandwidth = newBandwidth; command.audioSampleRate = newAudioSampleRate; bandwidthChanged.store(false); sampleRateChanged.store(false); audioSampleRateChanged.store(false); modemSettingsBuffered.clear(); //VSO: blocking workerQueue->push(command); } // Requested frequency is not center, shift it into the center! if ((currentFrequency - inp->frequency) != shiftFrequency) { shiftFrequency = currentFrequency - inp->frequency; if (abs(shiftFrequency) <= (int) ((double) (inp->sampleRate / 2) * 1.5)) { nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) inp->sampleRate))); } } if (cModem && cModemKit && abs(shiftFrequency) > (int) ((double) (inp->sampleRate / 2) * 1.5)) { continue; } // std::lock_guard < std::mutex > lock(inp->m_mutex); std::vector *data = &inp->data; if (data->size() && (inp->sampleRate == currentSampleRate) && cModem && cModemKit) { size_t bufSize = data->size(); if (in_buf_data.size() != bufSize) { if (in_buf_data.capacity() < bufSize) { in_buf_data.reserve(bufSize); out_buf_data.reserve(bufSize); } in_buf_data.resize(bufSize); out_buf_data.resize(bufSize); } in_buf_data.assign(inp->data.begin(), inp->data.end()); liquid_float_complex *in_buf = &in_buf_data[0]; liquid_float_complex *out_buf = &out_buf_data[0]; liquid_float_complex *temp_buf = NULL; if (shiftFrequency != 0) { if (shiftFrequency < 0) { nco_crcf_mix_block_up(freqShifter, in_buf, out_buf, bufSize); } else { nco_crcf_mix_block_down(freqShifter, in_buf, out_buf, bufSize); } temp_buf = in_buf; in_buf = out_buf; out_buf = temp_buf; } DemodulatorThreadPostIQDataPtr resamp = buffers.getBuffer(); size_t out_size = ceil((double) (bufSize) * iqResampleRatio) + 512; if (resampledData.size() != out_size) { if (resampledData.capacity() < out_size) { resampledData.reserve(out_size); } resampledData.resize(out_size); } unsigned int numWritten; msresamp_crcf_execute(iqResampler, in_buf, bufSize, &resampledData[0], &numWritten); resamp->data.assign(resampledData.begin(), resampledData.begin() + numWritten); resamp->modemType = cModem->getType(); resamp->modemName = cModem->getName(); resamp->modem = cModem; resamp->modemKit = cModemKit; resamp->sampleRate = currentBandwidth; //VSO: blocking push iqOutputQueue->push(resamp); } DemodulatorWorkerThreadResult result; //process all worker results until while (!stopping && workerResults->try_pop(result)) { switch (result.cmd) { case DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS: if (result.iqResampler) { if (iqResampler) { msresamp_crcf_destroy(iqResampler); } iqResampler = result.iqResampler; iqResampleRatio = result.iqResampleRatio; } if (result.modem != nullptr) { cModem = result.modem; #if ENABLE_DIGITAL_LAB if (cModem->getType() == "digital") { ModemDigital *mDigi = (ModemDigital *)cModem; mDigi->setOutput(parent->getOutput()); } #endif } if (result.modemKit != nullptr) { cModemKit = result.modemKit; currentAudioSampleRate = cModemKit->audioSampleRate; } if (result.bandwidth) { currentBandwidth = result.bandwidth; } if (result.sampleRate) { currentSampleRate = result.sampleRate; } if (result.modemName != "") { demodType = result.modemName; demodTypeChanged.store(false); } shiftFrequency = inp->frequency-1; initialized.store(cModem != nullptr); break; default: break; } } //end while if ((cModem != nullptr) && modemSettingsChanged.load()) { cModem->writeSettings(modemSettingsBuffered); modemSettingsBuffered.clear(); modemSettingsChanged.store(false); } } //end while stopping iqOutputQueue->flush(); iqInputQueue->flush(); } void DemodulatorPreThread::setDemodType(std::string demodType) { newDemodType = demodType; demodTypeChanged.store(true); } std::string DemodulatorPreThread::getDemodType() { if (demodTypeChanged.load()) { return newDemodType; } return demodType; } void DemodulatorPreThread::setFrequency(long long freq) { frequencyChanged.store(true); newFrequency = freq; } long long DemodulatorPreThread::getFrequency() { if (frequencyChanged.load()) { return newFrequency; } return currentFrequency; } void DemodulatorPreThread::setSampleRate(long long sampleRate) { sampleRateChanged.store(true); newSampleRate = sampleRate; } long long DemodulatorPreThread::getSampleRate() { if (sampleRateChanged.load()) { return newSampleRate; } return currentSampleRate; } void DemodulatorPreThread::setBandwidth(int bandwidth) { bandwidthChanged.store(true); newBandwidth = bandwidth; } int DemodulatorPreThread::getBandwidth() { if (bandwidthChanged.load()) { return newBandwidth; } return currentBandwidth; } void DemodulatorPreThread::setAudioSampleRate(int rate) { audioSampleRateChanged.store(true); newAudioSampleRate = rate; } int DemodulatorPreThread::getAudioSampleRate() { if (audioSampleRateChanged.load()) { return newAudioSampleRate; } return currentAudioSampleRate; } void DemodulatorPreThread::terminate() { //make non-blocking calls to be sure threads are flagged for termination. IOThread::terminate(); workerThread->terminate(); //unblock the push() iqOutputQueue->flush(); iqInputQueue->flush(); //wait blocking for termination here, it could be long with lots of modems and we MUST terminate properly, //else better kill the whole application... workerThread->isTerminated(5000); t_Worker->join(); delete t_Worker; t_Worker = nullptr; delete workerThread; workerThread = nullptr; } Modem *DemodulatorPreThread::getModem() { return cModem; } ModemKit *DemodulatorPreThread::getModemKit() { return cModemKit; } std::string DemodulatorPreThread::readModemSetting(std::string setting) { if (cModem) { return cModem->readSetting(setting); } else if (modemSettingsBuffered.find(setting) != modemSettingsBuffered.end()) { return modemSettingsBuffered[setting]; } return ""; } void DemodulatorPreThread::writeModemSetting(std::string setting, std::string value) { modemSettingsBuffered[setting] = value; modemSettingsChanged.store(true); } ModemSettings DemodulatorPreThread::readModemSettings() { if (cModem) { return cModem->readSettings(); } else { return modemSettingsBuffered; } } void DemodulatorPreThread::writeModemSettings(ModemSettings settings) { modemSettingsBuffered = settings; modemSettingsChanged.store(true); } CubicSDR-0.2.3/src/demod/DemodulatorPreThread.h000066400000000000000000000043321322677621400212340ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include "CubicSDRDefs.h" #include "DemodDefs.h" #include "DemodulatorWorkerThread.h" class DemodulatorInstance; class DemodulatorPreThread : public IOThread { public: DemodulatorPreThread(DemodulatorInstance* parent); virtual ~DemodulatorPreThread(); virtual void run(); void setDemodType(std::string demodType); std::string getDemodType(); void setFrequency(long long sampleRate); long long getFrequency(); void setSampleRate(long long sampleRate); long long getSampleRate(); void setBandwidth(int bandwidth); int getBandwidth(); void setAudioSampleRate(int rate); int getAudioSampleRate(); bool isInitialized(); virtual void terminate(); Modem *getModem(); ModemKit *getModemKit(); std::string readModemSetting(std::string setting); void writeModemSetting(std::string setting, std::string value); ModemSettings readModemSettings(); void writeModemSettings(ModemSettings settings); protected: DemodulatorInstance* parent; msresamp_crcf iqResampler; double iqResampleRatio; std::vector resampledData; Modem *cModem; ModemKit *cModemKit; std::atomic_llong currentSampleRate, newSampleRate; std::atomic_llong currentFrequency, newFrequency; std::atomic_int currentBandwidth, newBandwidth; std::atomic_int currentAudioSampleRate, newAudioSampleRate; std::atomic_bool sampleRateChanged, frequencyChanged, bandwidthChanged, audioSampleRateChanged; ModemSettings modemSettingsBuffered; std::atomic_bool modemSettingsChanged; nco_crcf freqShifter; int shiftFrequency; std::atomic_bool initialized; std::atomic_bool demodTypeChanged; std::string demodType; std::string newDemodType; DemodulatorWorkerThread *workerThread; std::thread *t_Worker; DemodulatorThreadWorkerCommandQueuePtr workerQueue; DemodulatorThreadWorkerResultQueuePtr workerResults; DemodulatorThreadInputQueuePtr iqInputQueue; DemodulatorThreadPostInputQueuePtr iqOutputQueue; }; CubicSDR-0.2.3/src/demod/DemodulatorThread.cpp000066400000000000000000000344171322677621400211270ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "CubicSDRDefs.h" #include "DemodulatorThread.h" #include "DemodulatorInstance.h" #include "CubicSDR.h" #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //50 ms #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) #ifdef __APPLE__ #include #endif DemodulatorInstance* DemodulatorThread::squelchLock(nullptr); std::mutex DemodulatorThread::squelchLockMutex; DemodulatorThread::DemodulatorThread(DemodulatorInstance* parent) : IOThread(), outputBuffers("DemodulatorThreadBuffers"), squelchLevel(-100), signalLevel(-100), signalFloor(-30), signalCeil(30), squelchEnabled(false) { demodInstance = parent; muted.store(false); squelchBreak = false; } DemodulatorThread::~DemodulatorThread() { releaseSquelchLock(demodInstance); } void DemodulatorThread::onBindOutput(std::string name, ThreadQueueBasePtr threadQueue) { if (name == "AudioVisualOutput") { //protects because it may be changed at runtime std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue); audioVisOutputQueue = std::static_pointer_cast(threadQueue); } if (name == "AudioSink") { std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue); audioSinkOutputQueue = std::static_pointer_cast(threadQueue); } } double DemodulatorThread::abMagnitude(float inphase, float quadrature) { // cast to double, so we keep precision despite the **2 op later. double dinphase = (double)inphase; double dquadrature = (double)quadrature; //sqrt() has been an insanely fast intrinsic for years, use it ! return sqrt(dinphase * dinphase + dquadrature * dquadrature); } double DemodulatorThread::linearToDb(double linear) { #define SMALL 1e-20 if (linear <= SMALL) { linear = double(SMALL); } return 20.0 * log10(linear); } void DemodulatorThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread int priority = sched_get_priority_max( SCHED_FIFO )-1; sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif // std::cout << "Demodulator thread started.." << std::endl; iqInputQueue = std::static_pointer_cast(getInputQueue("IQDataInput")); audioOutputQueue = std::static_pointer_cast(getOutputQueue("AudioDataOutput")); threadQueueControl = std::static_pointer_cast(getInputQueue("ControlQueue")); ModemIQData modemData; while (!stopping) { DemodulatorThreadPostIQDataPtr inp; if (!iqInputQueue->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) { continue; } size_t bufSize = inp->data.size(); if (!bufSize) { continue; } if (inp->modemKit && inp->modemKit != cModemKit) { if (cModemKit != nullptr) { cModem->disposeKit(cModemKit); } cModemKit = inp->modemKit; } if (inp->modem && inp->modem != cModem) { delete cModem; cModem = inp->modem; } if (!cModem || !cModemKit) { continue; } std::vector *inputData; inputData = &inp->data; modemData.sampleRate = inp->sampleRate; modemData.data.assign(inputData->begin(), inputData->end()); AudioThreadInputPtr ati = nullptr; ModemAnalog *modemAnalog = (cModem->getType() == "analog")?((ModemAnalog *)cModem):nullptr; ModemDigital *modemDigital = (cModem->getType() == "digital")?((ModemDigital *)cModem):nullptr; if (modemAnalog != nullptr) { ati = outputBuffers.getBuffer(); ati->sampleRate = cModemKit->audioSampleRate; ati->inputRate = inp->sampleRate; } else if (modemDigital != nullptr) { ati = outputBuffers.getBuffer(); ati->sampleRate = cModemKit->sampleRate; ati->inputRate = inp->sampleRate; ati->data.resize(0); } cModem->demodulate(cModemKit, &modemData, ati.get()); double currentSignalLevel = 0; double sampleTime = double(inp->data.size()) / double(inp->sampleRate); if (audioOutputQueue != nullptr && ati && ati->data.size()) { double accum = 0; if (cModem->useSignalOutput()) { for (auto i : ati->data) { accum += abMagnitude(i, 0.0); } currentSignalLevel = linearToDb(accum / double(ati->data.size())); } else { for (auto i : inp->data) { accum += abMagnitude(i.real, i.imag); } currentSignalLevel = linearToDb(accum / double(inp->data.size())); } float sf = signalFloor.load(), sc = signalCeil.load(), sl = squelchLevel.load(); if (currentSignalLevel > sc) { sc = currentSignalLevel; } if (currentSignalLevel < sf) { sf = currentSignalLevel; } if (sl+1.0f > sc) { sc = sl+1.0f; } if ((sf+2.0f) > sc) { sc = sf+2.0f; } sc -= (sc - (currentSignalLevel + 2.0f)) * sampleTime * 0.05f; sf += ((currentSignalLevel - 5.0f) - sf) * sampleTime * 0.15f; signalFloor.store(sf); signalCeil.store(sc); } if (currentSignalLevel > signalLevel) { signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.5; } else { signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.05 * sampleTime * 30.0; } bool squelched = squelchEnabled && (signalLevel < squelchLevel); if (squelchEnabled) { if (!squelched && !squelchBreak) { if (wxGetApp().getSoloMode() && !wxGetApp().getAppFrame()->isUserDemodBusy()) { std::lock_guard < std::mutex > lock(squelchLockMutex); if (squelchLock == nullptr) { squelchLock = demodInstance; wxGetApp().getDemodMgr().setActiveDemodulator(nullptr); wxGetApp().getDemodMgr().setActiveDemodulatorByRawPointer(demodInstance, false); squelchBreak = true; demodInstance->getVisualCue()->triggerSquelchBreak(120); } } else { squelchBreak = true; demodInstance->getVisualCue()->triggerSquelchBreak(120); } } else if (squelched && squelchBreak) { releaseSquelchLock(demodInstance); squelchBreak = false; } } //compute audio peak: if (audioOutputQueue != nullptr && ati) { ati->peak = 0; for (auto data_i : ati->data) { float p = fabs(data_i); if (p > ati->peak) { ati->peak = p; } } } //attach squelch flag to samples, to be used by audio sink. if (ati) { ati->is_squelch_active = squelched; } //At that point, capture the current state of audioVisOutputQueue in a local //variable, and works with it with now on until the next while-turn. DemodulatorThreadOutputQueuePtr localAudioVisOutputQueue = nullptr; { std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue); localAudioVisOutputQueue = audioVisOutputQueue; } if (!squelched && (ati || modemDigital) && localAudioVisOutputQueue != nullptr && localAudioVisOutputQueue->empty()) { AudioThreadInputPtr ati_vis = std::make_shared(); ati_vis->sampleRate = inp->sampleRate; ati_vis->inputRate = inp->sampleRate; size_t num_vis = DEMOD_VIS_SIZE; if (modemDigital) { if (ati) { // TODO: handle digital modems with audio output ati = nullptr; } ati_vis->data.resize(inputData->size()); ati_vis->channels = 2; for (int i = 0, iMax = inputData->size() / 2; i < iMax; i++) { ati_vis->data[i * 2] = (*inputData)[i].real; ati_vis->data[i * 2 + 1] = (*inputData)[i].imag; } ati_vis->type = 2; } else if (ati->channels==2) { ati_vis->channels = 2; int stereoSize = ati->data.size(); if (stereoSize > DEMOD_VIS_SIZE * 2) { stereoSize = DEMOD_VIS_SIZE * 2; } ati_vis->data.resize(stereoSize); if (inp->modemName == "I/Q") { for (int i = 0; i < stereoSize / 2; i++) { ati_vis->data[i] = (*inputData)[i].real * 0.75; ati_vis->data[i + stereoSize / 2] = (*inputData)[i].imag * 0.75; } } else { for (int i = 0; i < stereoSize / 2; i++) { ati_vis->inputRate = cModemKit->audioSampleRate; ati_vis->sampleRate = 36000; ati_vis->data[i] = ati->data[i * 2]; ati_vis->data[i + stereoSize / 2] = ati->data[i * 2 + 1]; } } ati_vis->type = 1; } else { size_t numAudioWritten = ati->data.size(); ati_vis->channels = 1; std::vector *demodOutData = (modemAnalog != nullptr)?modemAnalog->getDemodOutputData():nullptr; if ((numAudioWritten > bufSize) || (demodOutData == nullptr)) { ati_vis->inputRate = cModemKit->audioSampleRate; if (num_vis > numAudioWritten) { num_vis = numAudioWritten; } ati_vis->data.assign(ati->data.begin(), ati->data.begin() + num_vis); } else { if (num_vis > demodOutData->size()) { num_vis = demodOutData->size(); } ati_vis->data.assign(demodOutData->begin(), demodOutData->begin() + num_vis); } ati_vis->type = 0; } if (!localAudioVisOutputQueue->try_push(ati_vis)) { //non-blocking push needed for audio vis out std::cout << "DemodulatorThread::run() cannot push ati_vis into localAudioVisOutputQueue, is full !" << std::endl; std::this_thread::yield(); } } if (!squelched && ati != nullptr) { if (!muted.load() && (!wxGetApp().getSoloMode() || (demodInstance == wxGetApp().getDemodMgr().getLastActiveDemodulator().get()))) { //non-blocking push needed for audio out if (!audioOutputQueue->try_push(ati)) { std::cout << "DemodulatorThread::run() cannot push ati into audioOutputQueue, is full !" << std::endl; std::this_thread::yield(); } } } // Capture audioSinkOutputQueue state in a local variable DemodulatorThreadOutputQueuePtr localAudioSinkOutputQueue = nullptr; { std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue); localAudioSinkOutputQueue = audioSinkOutputQueue; } //Push to audio sink, if any: if (ati && localAudioSinkOutputQueue != nullptr) { if (!localAudioSinkOutputQueue->try_push(ati)) { std::cout << "DemodulatorThread::run() cannot push ati into audioSinkOutputQueue, is full !" << std::endl; std::this_thread::yield(); } } DemodulatorThreadControlCommand command; //empty command queue, execute commands while (threadQueueControl->try_pop(command)) { switch (command.cmd) { case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_ON: squelchEnabled = true; break; case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_SQUELCH_OFF: squelchEnabled = false; break; default: break; } } } // end while !stopping // Purge any unused inputs, with a non-blocking pop iqInputQueue->flush(); audioOutputQueue->flush(); // std::cout << "Demodulator thread done." << std::endl; } void DemodulatorThread::terminate() { IOThread::terminate(); //unblock the curretly blocked push() iqInputQueue->flush(); audioOutputQueue->flush(); threadQueueControl->flush(); } bool DemodulatorThread::isMuted() { return muted.load(); } void DemodulatorThread::setMuted(bool muted) { this->muted.store(muted); } float DemodulatorThread::getSignalLevel() { return signalLevel.load(); } float DemodulatorThread::getSignalFloor() { return signalFloor.load(); } float DemodulatorThread::getSignalCeil() { return signalCeil.load(); } void DemodulatorThread::setSquelchLevel(float signal_level_in) { if (!squelchEnabled) { squelchEnabled = true; } squelchLevel = signal_level_in; } float DemodulatorThread::getSquelchLevel() { return squelchLevel; } bool DemodulatorThread::getSquelchBreak() { return squelchBreak; } void DemodulatorThread::releaseSquelchLock(DemodulatorInstance* inst) { std::lock_guard < std::mutex > lock(squelchLockMutex); if (inst == nullptr || squelchLock == inst) { squelchLock = nullptr; } } CubicSDR-0.2.3/src/demod/DemodulatorThread.h000066400000000000000000000035261322677621400205710ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include "DemodDefs.h" #include "AudioThread.h" #include "Modem.h" #define DEMOD_VIS_SIZE 2048 #define DEMOD_SIGNAL_MIN -30 #define DEMOD_SIGNAL_MAX 30 class DemodulatorInstance; class DemodulatorThread : public IOThread { public: DemodulatorThread(DemodulatorInstance* parent); virtual ~DemodulatorThread(); void onBindOutput(std::string name, ThreadQueueBasePtr threadQueue); virtual void run(); virtual void terminate(); void setMuted(bool state); bool isMuted(); float getSignalLevel(); float getSignalCeil(); float getSignalFloor(); void setSquelchLevel(float signal_level_in); float getSquelchLevel(); bool getSquelchBreak(); static void releaseSquelchLock(DemodulatorInstance* inst); protected: double abMagnitude(float inphase, float quadrature); double linearToDb(double linear); DemodulatorInstance* demodInstance; ReBuffer outputBuffers; std::atomic_bool muted; std::atomic squelchLevel; std::atomic signalLevel, signalFloor, signalCeil; bool squelchEnabled, squelchBreak; static DemodulatorInstance* squelchLock; static std::mutex squelchLockMutex; Modem *cModem = nullptr; ModemKit *cModemKit = nullptr; DemodulatorThreadPostInputQueuePtr iqInputQueue; AudioThreadInputQueuePtr audioOutputQueue; DemodulatorThreadOutputQueuePtr audioVisOutputQueue; DemodulatorThreadControlCommandQueuePtr threadQueueControl; DemodulatorThreadOutputQueuePtr audioSinkOutputQueue = nullptr; //protects the audioVisOutputQueue dynamic binding change at runtime (in DemodulatorMgr) std::mutex m_mutexAudioVisOutputQueue; }; CubicSDR-0.2.3/src/demod/DemodulatorWorkerThread.cpp000066400000000000000000000112201322677621400223040ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "DemodulatorWorkerThread.h" #include "CubicSDRDefs.h" #include "CubicSDR.h" #include //50 ms #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) DemodulatorWorkerThread::DemodulatorWorkerThread() : IOThread(), cModem(nullptr), cModemKit(nullptr) { } DemodulatorWorkerThread::~DemodulatorWorkerThread() { } void DemodulatorWorkerThread::run() { // std::cout << "Demodulator worker thread started.." << std::endl; commandQueue = std::static_pointer_cast(getInputQueue("WorkerCommandQueue")); resultQueue = std::static_pointer_cast(getOutputQueue("WorkerResultQueue")); while (!stopping) { bool filterChanged = false; bool makeDemod = false; DemodulatorWorkerThreadCommand filterCommand, demodCommand; DemodulatorWorkerThreadCommand command; bool done = false; //Beware of the subtility here, //we are waiting for the first command to show up (blocking!) //then consuming the commands until done. while (!done && !stopping) { if (!commandQueue->pop(command, HEARTBEAT_CHECK_PERIOD_MICROS)) { continue; } switch (command.cmd) { case DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_BUILD_FILTERS: filterChanged = true; filterCommand = command; break; case DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_MAKE_DEMOD: makeDemod = true; demodCommand = command; break; default: break; } done = commandQueue->empty(); } //end while done. if ((makeDemod || filterChanged) && !stopping) { DemodulatorWorkerThreadResult result(DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS); if (filterCommand.sampleRate) { result.sampleRate = filterCommand.sampleRate; } if (makeDemod) { cModem = Modem::makeModem(demodCommand.demodType); cModemName = cModem->getName(); cModemType = cModem->getType(); if (demodCommand.settings.size()) { cModem->writeSettings(demodCommand.settings); } result.sampleRate = demodCommand.sampleRate; wxGetApp().getAppFrame()->notifyUpdateModemProperties(); } result.modem = cModem; if (makeDemod && demodCommand.bandwidth && demodCommand.audioSampleRate) { if (cModem != nullptr) { result.bandwidth = cModem->checkSampleRate(demodCommand.bandwidth, demodCommand.audioSampleRate); cModemKit = cModem->buildKit(result.bandwidth, demodCommand.audioSampleRate); } else { cModemKit = nullptr; } } else if (filterChanged && filterCommand.bandwidth && filterCommand.audioSampleRate) { if (cModem != nullptr) { result.bandwidth = cModem->checkSampleRate(filterCommand.bandwidth, filterCommand.audioSampleRate); cModemKit = cModem->buildKit(result.bandwidth, filterCommand.audioSampleRate); } else { cModemKit = nullptr; } } else if (makeDemod) { cModemKit = nullptr; } if (cModem != nullptr) { cModem->clearRebuildKit(); } float As = 60.0f; // stop-band attenuation [dB] if (cModem && result.sampleRate && result.bandwidth) { result.bandwidth = cModem->checkSampleRate(result.bandwidth, makeDemod?demodCommand.audioSampleRate:filterCommand.audioSampleRate); result.iqResampleRatio = (double) (result.bandwidth) / (double) result.sampleRate; result.iqResampler = msresamp_crcf_create(result.iqResampleRatio, As); } result.modemKit = cModemKit; result.modemType = cModemType; result.modemName = cModemName; //VSO: blocking push resultQueue->push(result); } } // std::cout << "Demodulator worker thread done." << std::endl; } void DemodulatorWorkerThread::terminate() { IOThread::terminate(); //unblock the push() resultQueue->flush(); commandQueue->flush(); } CubicSDR-0.2.3/src/demod/DemodulatorWorkerThread.h000066400000000000000000000054311322677621400217600ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include "liquid/liquid.h" #include "AudioThread.h" #include "ThreadBlockingQueue.h" #include "CubicSDRDefs.h" #include "Modem.h" class DemodulatorWorkerThreadResult { public: enum DemodulatorThreadResultEnum { DEMOD_WORKER_THREAD_RESULT_NULL, DEMOD_WORKER_THREAD_RESULT_FILTERS }; DemodulatorWorkerThreadResult() : cmd(DEMOD_WORKER_THREAD_RESULT_NULL), iqResampler(nullptr), iqResampleRatio(0), sampleRate(0), bandwidth(0), modemKit(nullptr), modemType("") { } DemodulatorWorkerThreadResult(DemodulatorThreadResultEnum cmd) : DemodulatorWorkerThreadResult() { this->cmd = cmd; } DemodulatorThreadResultEnum cmd; msresamp_crcf iqResampler; double iqResampleRatio; long long sampleRate; unsigned int bandwidth; Modem *modem; ModemKit *modemKit; std::string modemType; std::string modemName; }; class DemodulatorWorkerThreadCommand { public: enum DemodulatorThreadCommandEnum { DEMOD_WORKER_THREAD_CMD_NULL, DEMOD_WORKER_THREAD_CMD_BUILD_FILTERS, DEMOD_WORKER_THREAD_CMD_MAKE_DEMOD }; DemodulatorWorkerThreadCommand() : cmd(DEMOD_WORKER_THREAD_CMD_NULL), frequency(0), sampleRate(0), bandwidth(0), audioSampleRate(0), demodType("") { } DemodulatorWorkerThreadCommand(DemodulatorThreadCommandEnum cmd) : cmd(cmd), frequency(0), sampleRate(0), bandwidth(0), audioSampleRate(0), demodType("") { } DemodulatorThreadCommandEnum cmd; long long frequency; long long sampleRate; unsigned int bandwidth; unsigned int audioSampleRate; std::string demodType; ModemSettings settings; }; typedef ThreadBlockingQueue DemodulatorThreadWorkerCommandQueue; typedef ThreadBlockingQueue DemodulatorThreadWorkerResultQueue; typedef std::shared_ptr DemodulatorThreadWorkerCommandQueuePtr; typedef std::shared_ptr DemodulatorThreadWorkerResultQueuePtr; class DemodulatorWorkerThread : public IOThread { public: DemodulatorWorkerThread(); virtual ~DemodulatorWorkerThread(); virtual void run(); void setCommandQueue(DemodulatorThreadWorkerCommandQueuePtr tQueue) { commandQueue = tQueue; } void setResultQueue(DemodulatorThreadWorkerResultQueuePtr tQueue) { resultQueue = tQueue; } virtual void terminate(); protected: DemodulatorThreadWorkerCommandQueuePtr commandQueue; DemodulatorThreadWorkerResultQueuePtr resultQueue; Modem *cModem; ModemKit *cModemKit; std::string cModemType; std::string cModemName; }; CubicSDR-0.2.3/src/forms/000077500000000000000000000000001322677621400150415ustar00rootroot00000000000000CubicSDR-0.2.3/src/forms/Bookmark/000077500000000000000000000000001322677621400166065ustar00rootroot00000000000000CubicSDR-0.2.3/src/forms/Bookmark/BookmarkPanel.cpp000066400000000000000000000210021322677621400220320ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "BookmarkPanel.h" /////////////////////////////////////////////////////////////////////////// BookmarkPanel::BookmarkPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : wxPanel(parent, id, pos, size, style) { wxBoxSizer* bSizer1; bSizer1 = new wxBoxSizer(wxVERTICAL); m_searchText = new wxTextCtrl(this, wxID_ANY, wxT("Search.."), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); bSizer1->Add(m_searchText, 0, wxALL | wxEXPAND, 5); m_clearSearchButton = new wxButton(this, wxID_ANY, wxT("Clear Search"), wxDefaultPosition, wxDefaultSize, 0); bSizer1->Add(m_clearSearchButton, 0, wxBOTTOM | wxEXPAND | wxLEFT | wxRIGHT, 5); m_treeView = new wxTreeCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE | wxTR_HAS_VARIABLE_ROW_HEIGHT | wxTR_HIDE_ROOT | wxTR_SINGLE); bSizer1->Add(m_treeView, 1, wxEXPAND, 5); m_propPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); wxFlexGridSizer* fgPropSizer; fgPropSizer = new wxFlexGridSizer(0, 2, 0, 0); fgPropSizer->AddGrowableCol(1); fgPropSizer->SetFlexibleDirection(wxBOTH); fgPropSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); m_labelLabel = new wxStaticText(m_propPanel, wxID_ANY, wxT("Label"), wxDefaultPosition, wxDefaultSize, 0); m_labelLabel->Wrap(-1); fgPropSizer->Add(m_labelLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 5); m_labelText = new wxTextCtrl(m_propPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); fgPropSizer->Add(m_labelText, 0, wxALL | wxEXPAND, 5); m_frequencyLabel = new wxStaticText(m_propPanel, wxID_ANY, wxT("Freq"), wxDefaultPosition, wxDefaultSize, 0); m_frequencyLabel->Wrap(-1); fgPropSizer->Add(m_frequencyLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 5); m_frequencyVal = new wxStaticText(m_propPanel, wxID_ANY, wxT("FrequencyVal"), wxDefaultPosition, wxDefaultSize, 0); m_frequencyVal->Wrap(-1); fgPropSizer->Add(m_frequencyVal, 0, wxALL, 5); m_bandwidthLabel = new wxStaticText(m_propPanel, wxID_ANY, wxT("BW"), wxDefaultPosition, wxDefaultSize, 0); m_bandwidthLabel->Wrap(-1); fgPropSizer->Add(m_bandwidthLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 5); m_bandwidthVal = new wxStaticText(m_propPanel, wxID_ANY, wxT("BandwidthVal"), wxDefaultPosition, wxDefaultSize, 0); m_bandwidthVal->Wrap(-1); fgPropSizer->Add(m_bandwidthVal, 0, wxALL, 5); m_modulationLabel = new wxStaticText(m_propPanel, wxID_ANY, wxT("Type"), wxDefaultPosition, wxDefaultSize, 0); m_modulationLabel->Wrap(-1); fgPropSizer->Add(m_modulationLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 5); m_modulationVal = new wxStaticText(m_propPanel, wxID_ANY, wxT("TypeVal"), wxDefaultPosition, wxDefaultSize, 0); m_modulationVal->Wrap(-1); fgPropSizer->Add(m_modulationVal, 0, wxALL, 5); m_propPanel->SetSizer(fgPropSizer); m_propPanel->Layout(); fgPropSizer->Fit(m_propPanel); bSizer1->Add(m_propPanel, 0, wxALL | wxBOTTOM | wxEXPAND | wxTOP, 5); m_buttonPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); wxBoxSizer* m_buttonPanelSizer; m_buttonPanelSizer = new wxBoxSizer(wxVERTICAL); m_buttonPanel->SetSizer(m_buttonPanelSizer); m_buttonPanel->Layout(); m_buttonPanelSizer->Fit(m_buttonPanel); bSizer1->Add(m_buttonPanel, 0, wxALL | wxEXPAND, 5); this->SetSizer(bSizer1); this->Layout(); m_updateTimer.SetOwner(this, wxID_ANY); // Connect Events this->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(BookmarkPanel::onEnterWindow)); this->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(BookmarkPanel::onLeaveWindow)); this->Connect(wxEVT_MOTION, wxMouseEventHandler(BookmarkPanel::onMotion)); m_searchText->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(BookmarkPanel::onSearchTextFocus), NULL, this); m_searchText->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(BookmarkPanel::onSearchText), NULL, this); m_clearSearchButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BookmarkPanel::onClearSearch), NULL, this); m_treeView->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(BookmarkPanel::onEnterWindow), NULL, this); m_treeView->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(BookmarkPanel::onLeaveWindow), NULL, this); m_treeView->Connect(wxEVT_MOTION, wxMouseEventHandler(BookmarkPanel::onMotion), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEventHandler(BookmarkPanel::onTreeBeginDrag), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_END_DRAG, wxTreeEventHandler(BookmarkPanel::onTreeEndDrag), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEventHandler(BookmarkPanel::onTreeActivate), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEventHandler(BookmarkPanel::onTreeCollapse), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEventHandler(BookmarkPanel::onTreeExpanded), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEventHandler(BookmarkPanel::onTreeItemGetTooltip), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEventHandler(BookmarkPanel::onTreeItemMenu), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(BookmarkPanel::onTreeSelect), NULL, this); m_treeView->Connect(wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEventHandler(BookmarkPanel::onTreeSelectChanging), NULL, this); m_labelText->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(BookmarkPanel::onLabelText), NULL, this); m_frequencyVal->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(BookmarkPanel::onDoubleClickFreq), NULL, this); m_bandwidthVal->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(BookmarkPanel::onDoubleClickBandwidth), NULL, this); this->Connect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(BookmarkPanel::onUpdateTimer)); } BookmarkPanel::~BookmarkPanel() { // Disconnect Events this->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(BookmarkPanel::onEnterWindow)); this->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(BookmarkPanel::onLeaveWindow)); this->Disconnect(wxEVT_MOTION, wxMouseEventHandler(BookmarkPanel::onMotion)); m_searchText->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(BookmarkPanel::onSearchTextFocus), NULL, this); m_searchText->Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(BookmarkPanel::onSearchText), NULL, this); m_clearSearchButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BookmarkPanel::onClearSearch), NULL, this); m_treeView->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(BookmarkPanel::onEnterWindow), NULL, this); m_treeView->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(BookmarkPanel::onLeaveWindow), NULL, this); m_treeView->Disconnect(wxEVT_MOTION, wxMouseEventHandler(BookmarkPanel::onMotion), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEventHandler(BookmarkPanel::onTreeBeginDrag), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_END_DRAG, wxTreeEventHandler(BookmarkPanel::onTreeEndDrag), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEventHandler(BookmarkPanel::onTreeActivate), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEventHandler(BookmarkPanel::onTreeCollapse), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEventHandler(BookmarkPanel::onTreeExpanded), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEventHandler(BookmarkPanel::onTreeItemGetTooltip), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEventHandler(BookmarkPanel::onTreeItemMenu), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(BookmarkPanel::onTreeSelect), NULL, this); m_treeView->Disconnect(wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEventHandler(BookmarkPanel::onTreeSelectChanging), NULL, this); m_labelText->Disconnect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(BookmarkPanel::onLabelText), NULL, this); m_frequencyVal->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(BookmarkPanel::onDoubleClickFreq), NULL, this); m_bandwidthVal->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(BookmarkPanel::onDoubleClickBandwidth), NULL, this); this->Disconnect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(BookmarkPanel::onUpdateTimer)); } CubicSDR-0.2.3/src/forms/Bookmark/BookmarkPanel.fbp000066400000000000000000002432611322677621400220340ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect BookmarkPanel 1000 none 0 BookmarkPanel . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT 1 1 impl_virtual 0 wxID_ANY BookmarkPanel 169,471 wxTAB_TRAVERSAL onEnterWindow onLeaveWindow onMotion bSizer1 wxVERTICAL none 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_searchText 1 protected 1 Resizable 1 wxTE_PROCESS_ENTER 0 wxFILTER_NONE wxDefaultValidator Search.. onSearchTextFocus onSearchText 5 wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Clear Search 0 0 1 m_clearSearchButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator onClearSearch 5 wxEXPAND 1 1 1 1 1 1 0 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_treeView 1 protected 1 Resizable 1 wxTR_DEFAULT_STYLE|wxTR_HAS_VARIABLE_ROW_HEIGHT|wxTR_HIDE_ROOT|wxTR_SINGLE 0 onEnterWindow onLeaveWindow onMotion onTreeBeginDrag onTreeEndDrag onTreeActivate onTreeCollapse onTreeExpanded onTreeItemGetTooltip onTreeItemMenu onTreeSelect onTreeSelectChanging 5 wxALL|wxBOTTOM|wxEXPAND|wxTOP 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_propPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL 2 wxBOTH 1 0 fgPropSizer wxFLEX_GROWMODE_SPECIFIED none 0 0 5 wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Label 0 0 1 m_labelLabel 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_labelText 1 protected 1 Resizable 1 wxTE_PROCESS_ENTER 0 wxFILTER_NONE wxDefaultValidator onLabelText 5 wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Freq 0 0 1 m_frequencyLabel 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY FrequencyVal 0 0 1 m_frequencyVal 1 protected 1 Resizable 1 0 -1 onDoubleClickFreq 5 wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY BW 0 0 1 m_bandwidthLabel 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY BandwidthVal 0 0 1 m_bandwidthVal 1 protected 1 Resizable 1 0 -1 onDoubleClickBandwidth 5 wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Type 0 0 1 m_modulationLabel 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY TypeVal 0 0 1 m_modulationVal 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_buttonPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL m_buttonPanelSizer wxVERTICAL none 0 wxID_ANY m_updateTimer 0 500 protected onUpdateTimer CubicSDR-0.2.3/src/forms/Bookmark/BookmarkPanel.h000066400000000000000000000056651322677621400215200ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #ifndef __BOOKMARKPANEL_H__ #define __BOOKMARKPANEL_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Class BookmarkPanel /////////////////////////////////////////////////////////////////////////////// class BookmarkPanel : public wxPanel { private: protected: wxTextCtrl* m_searchText; wxButton* m_clearSearchButton; wxTreeCtrl* m_treeView; wxPanel* m_propPanel; wxStaticText* m_labelLabel; wxTextCtrl* m_labelText; wxStaticText* m_frequencyLabel; wxStaticText* m_frequencyVal; wxStaticText* m_bandwidthLabel; wxStaticText* m_bandwidthVal; wxStaticText* m_modulationLabel; wxStaticText* m_modulationVal; wxPanel* m_buttonPanel; wxTimer m_updateTimer; // Virtual event handlers, overide them in your derived class virtual void onEnterWindow(wxMouseEvent& event) { event.Skip(); } virtual void onLeaveWindow(wxMouseEvent& event) { event.Skip(); } virtual void onMotion(wxMouseEvent& event) { event.Skip(); } virtual void onSearchTextFocus(wxMouseEvent& event) { event.Skip(); } virtual void onSearchText(wxCommandEvent& event) { event.Skip(); } virtual void onClearSearch(wxCommandEvent& event) { event.Skip(); } virtual void onTreeBeginDrag(wxTreeEvent& event) { event.Skip(); } virtual void onTreeEndDrag(wxTreeEvent& event) { event.Skip(); } virtual void onTreeActivate(wxTreeEvent& event) { event.Skip(); } virtual void onTreeCollapse(wxTreeEvent& event) { event.Skip(); } virtual void onTreeExpanded(wxTreeEvent& event) { event.Skip(); } virtual void onTreeItemGetTooltip(wxTreeEvent& event) { event.Skip(); } virtual void onTreeItemMenu(wxTreeEvent& event) { event.Skip(); } virtual void onTreeSelect(wxTreeEvent& event) { event.Skip(); } virtual void onTreeSelectChanging(wxTreeEvent& event) { event.Skip(); } virtual void onLabelText(wxCommandEvent& event) { event.Skip(); } virtual void onDoubleClickFreq(wxMouseEvent& event) { event.Skip(); } virtual void onDoubleClickBandwidth(wxMouseEvent& event) { event.Skip(); } virtual void onUpdateTimer(wxTimerEvent& event) { event.Skip(); } public: BookmarkPanel(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(169, 471), long style = wxTAB_TRAVERSAL); ~BookmarkPanel(); }; #endif //__BOOKMARKPANEL_H__ CubicSDR-0.2.3/src/forms/Bookmark/BookmarkView.cpp000066400000000000000000001434241322677621400217220ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include #include #include #include #include "BookmarkView.h" #include "CubicSDR.h" #include "ActionDialog.h" #define wxCONTEXT_ADD_GROUP_ID 1000 #define BOOKMARK_VIEW_CHOICE_DEFAULT "Bookmark.." #define BOOKMARK_VIEW_CHOICE_MOVE "Move to.." #define BOOKMARK_VIEW_CHOICE_NEW "(New Group..)" #define BOOKMARK_VIEW_STR_ADD_GROUP "Add Group" #define BOOKMARK_VIEW_STR_ADD_GROUP_DESC "Enter Group Name" #define BOOKMARK_VIEW_STR_UNNAMED "Unnamed" #define BOOKMARK_VIEW_STR_CLEAR_RECENT "Clear Recents" #define BOOKMARK_VIEW_STR_RENAME_GROUP "Rename Group" BookmarkViewVisualDragItem::BookmarkViewVisualDragItem(wxString labelValue) : wxDialog(NULL, wxID_ANY, L"", wxPoint(20,20), wxSize(-1,-1), wxSTAY_ON_TOP | wxALL ) { wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); wxStaticText *label = new wxStaticText( this, wxID_ANY, labelValue, wxDefaultPosition, wxDefaultSize, wxEXPAND ); sizer->Add(label, 1, wxALL | wxEXPAND, 5); SetSizerAndFit(sizer); Show(); } class ActionDialogRemoveBookmark : public ActionDialog { public: ActionDialogRemoveBookmark( BookmarkEntryPtr be ) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Remove Bookmark?")) { subject = be; m_questionText->SetLabelText(wxT("Are you sure you want to remove the bookmark\n '" + BookmarkMgr::getBookmarkEntryDisplayName(subject) + "'?")); } void doClickOK() { wxGetApp().getBookmarkMgr().removeBookmark(subject); wxGetApp().getBookmarkMgr().updateBookmarks(); } private: BookmarkEntryPtr subject; }; class ActionDialogRemoveGroup : public ActionDialog { public: ActionDialogRemoveGroup( std::string groupName ) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Remove Group?")) { subject = groupName; m_questionText->SetLabelText(wxT("Warning: Are you sure you want to remove the group\n '" + subject + "' AND ALL BOOKMARKS WITHIN IT?")); } void doClickOK() { wxGetApp().getBookmarkMgr().removeGroup(subject); wxGetApp().getBookmarkMgr().updateBookmarks(); } private: std::string subject; }; class ActionDialogRemoveRange : public ActionDialog { public: ActionDialogRemoveRange( BookmarkRangeEntryPtr rangeEnt ) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Remove Range?")) { subject = rangeEnt; std::wstring name = rangeEnt->label; if (name.length() == 0) { std::string wstr = frequencyToStr(rangeEnt->startFreq) + " - " + frequencyToStr(rangeEnt->endFreq); name = wxString(wstr).ToStdWstring(); } m_questionText->SetLabelText(L"Are you sure you want to remove the range\n '" + name + L"'?"); } void doClickOK() { wxGetApp().getBookmarkMgr().removeRange(subject); wxGetApp().getBookmarkMgr().updateActiveList(); } private: BookmarkRangeEntryPtr subject; }; class ActionDialogUpdateRange : public ActionDialog { public: ActionDialogUpdateRange( BookmarkRangeEntryPtr rangeEnt ) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Update Range?")) { subject = rangeEnt; std::wstring name = rangeEnt->label; if (name.length() == 0) { std::string wstr = frequencyToStr(rangeEnt->startFreq) + " - " + frequencyToStr(rangeEnt->endFreq); name = wxString(wstr).ToStdWstring(); } m_questionText->SetLabelText(L"Are you sure you want to update the range\n '" + name + L"' to the active range?"); } void doClickOK() { BookmarkRangeEntryPtr ue = BookmarkView::makeActiveRangeEntry(); subject->freq = ue->freq; subject->startFreq = ue->startFreq; subject->endFreq = ue->endFreq; wxGetApp().getBookmarkMgr().updateActiveList(); } private: BookmarkRangeEntryPtr subject; }; BookmarkView::BookmarkView( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : BookmarkPanel(parent, id, pos, size, style) { rootBranch = m_treeView->AddRoot("Root"); activeBranch = m_treeView->AppendItem(rootBranch, "Active"); rangeBranch = m_treeView->AppendItem(rootBranch, "View Ranges"); bookmarkBranch = m_treeView->AppendItem(rootBranch, "Bookmarks"); recentBranch = m_treeView->AppendItem(rootBranch, "Recents"); expandState["active"] = true; expandState["range"] = false; expandState["bookmark"] = true; expandState["recent"] = true; doUpdateActive.store(true); doUpdateBookmarks.store(true); bookmarkChoice = nullptr; dragItem = nullptr; dragItemId = nullptr; editingLabel = false; m_clearSearchButton->Hide(); hideProps(); m_updateTimer.Start(500); visualDragItem = nullptr; nextEnt = nullptr; nextDemod = nullptr; nextGroup = ""; mouseTracker.setTarget(this); } BookmarkView::~BookmarkView() { dragItem = nullptr; dragItemId = nullptr; editingLabel = false; visualDragItem = nullptr; nextEnt = nullptr; nextDemod = nullptr; } void BookmarkView::onUpdateTimer( wxTimerEvent& /* event */ ) { if (!this->IsShown()) { return; } if (doUpdateActive.load()) { doUpdateActiveList(); doUpdateActive.store(false); } if (doUpdateBookmarks.load()) { wxTreeItemId bmSel = refreshBookmarks(); if (bmSel) { m_treeView->SelectItem(bmSel); } doUpdateBookmarks.store(false); } } void BookmarkView::updateTheme() { wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); wxColour textColor(ThemeMgr::mgr.currentTheme->text); wxColour btn(ThemeMgr::mgr.currentTheme->button); wxColour btnHl(ThemeMgr::mgr.currentTheme->buttonHighlight); SetBackgroundColour(bgColor); m_treeView->SetBackgroundColour(bgColor); m_treeView->SetForegroundColour(textColor); m_propPanel->SetBackgroundColour(bgColor); m_propPanel->SetForegroundColour(textColor); m_buttonPanel->SetBackgroundColour(bgColor); m_buttonPanel->SetForegroundColour(textColor); m_labelLabel->SetForegroundColour(textColor); m_frequencyVal->SetForegroundColour(textColor); m_frequencyLabel->SetForegroundColour(textColor); m_bandwidthVal->SetForegroundColour(textColor); m_bandwidthLabel->SetForegroundColour(textColor); m_modulationVal->SetForegroundColour(textColor); m_modulationLabel->SetForegroundColour(textColor); refreshLayout(); } void BookmarkView::updateActiveList() { doUpdateActive.store(true); } void BookmarkView::updateBookmarks() { doUpdateBookmarks.store(true); } void BookmarkView::updateBookmarks(std::string group) { doUpdateBookmarkGroup.insert(group); doUpdateBookmarks.store(true); } bool BookmarkView::isKeywordMatch(std::wstring search_str, std::vector &keywords) { wstring str = search_str; std::transform(str.begin(), str.end(), str.begin(), towlower); for (auto k : keywords) { if (str.find(k) == wstring::npos) { return false; } } return true; } wxTreeItemId BookmarkView::refreshBookmarks() { //capture the previously selected item info BY COPY (because the original will be destroyed together with the destroyed tree items) to restore it again after //having rebuilding the whole tree. TreeViewItem* prevSel = itemToTVI(m_treeView->GetSelection()); TreeViewItem* prevSelCopy = nullptr; if (prevSel != NULL) { prevSelCopy = new TreeViewItem(*prevSel); } BookmarkNames groupNames; wxGetApp().getBookmarkMgr().getGroups(groupNames); doUpdateBookmarkGroup.clear(); wxTreeItemId bmSelFound = nullptr; std::map groupExpandState; bool searchState = (searchKeywords.size() != 0); groups.clear(); m_treeView->DeleteChildren(bookmarkBranch); bool bmExpandState = expandState["bookmark"]; for (auto gn_i : groupNames) { TreeViewItem* tvi = new TreeViewItem(); tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP; tvi->groupName = gn_i; wxTreeItemId group_itm = m_treeView->AppendItem(bookmarkBranch, gn_i); SetTreeItemData(group_itm, tvi); groups[gn_i] = group_itm; if (prevSelCopy != nullptr && prevSelCopy->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP && gn_i == prevSelCopy->groupName) { bmSelFound = group_itm; } else if (nextGroup != "" && gn_i == nextGroup) { bmSelFound = group_itm; nextGroup = ""; } } if (searchState || bmExpandState) { m_treeView->Expand(bookmarkBranch); } else { m_treeView->Collapse(bookmarkBranch); } for (auto gn_i : groupNames) { wxTreeItemId groupItem = groups[gn_i]; bool groupExpanded = searchState || wxGetApp().getBookmarkMgr().getExpandState(gn_i); const BookmarkList& bmList = wxGetApp().getBookmarkMgr().getBookmarks(gn_i); for (auto &bmEnt : bmList) { std::wstring labelVal = BookmarkMgr::getBookmarkEntryDisplayName(bmEnt); if (searchState) { std::string freqStr = frequencyToStr(bmEnt->frequency); std::string bwStr = frequencyToStr(bmEnt->bandwidth); std::wstring fullText = labelVal + L" " + bmEnt->label + L" " + std::to_wstring(bmEnt->frequency) + L" " + wxString(freqStr).ToStdWstring() + L" " + wxString(bwStr).ToStdWstring() + L" " + wxString(bmEnt->type).ToStdWstring(); if (!isKeywordMatch(fullText, searchKeywords)) { continue; } } TreeViewItem* tvi = new TreeViewItem(); tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK; tvi->bookmarkEnt = bmEnt; tvi->groupName = gn_i; wxTreeItemId itm = m_treeView->AppendItem(groupItem, labelVal); SetTreeItemData(itm, tvi); if (prevSelCopy != nullptr && prevSelCopy->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK && prevSelCopy->bookmarkEnt == bmEnt && groupExpanded) { bmSelFound = itm; } if (nextEnt == bmEnt) { bmSelFound = itm; nextEnt = nullptr; } } if (groupExpanded) { m_treeView->Expand(groupItem); } } delete prevSelCopy; return bmSelFound; } void BookmarkView::doUpdateActiveList() { auto demods = wxGetApp().getDemodMgr().getDemodulators(); auto lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator(); //capture the previously selected item info BY COPY (because the original will be destroyed together with the destroyed tree items) to restore it again after //having rebuilding the whole tree. TreeViewItem* prevSel = itemToTVI(m_treeView->GetSelection()); TreeViewItem* prevSelCopy = nullptr; if (prevSel != NULL) { prevSelCopy = new TreeViewItem(*prevSel); } // Actives m_treeView->DeleteChildren(activeBranch); bool activeExpandState = expandState["active"]; bool searchState = (searchKeywords.size() != 0); wxTreeItemId selItem = nullptr; for (auto demod_i : demods) { wxString activeLabel = BookmarkMgr::getActiveDisplayName(demod_i); if (searchState) { std::string freqStr = frequencyToStr(demod_i->getFrequency()); std::string bwStr = frequencyToStr(demod_i->getBandwidth()); std::string mtype = demod_i->getDemodulatorType(); std::wstring fullText = activeLabel.ToStdWstring() + L" " + demod_i->getDemodulatorUserLabel() + L" " + std::to_wstring(demod_i->getFrequency()) + L" " + wxString(freqStr).ToStdWstring() + L" " + wxString(bwStr).ToStdWstring() + L" " + wxString(mtype).ToStdWstring(); if (!isKeywordMatch(fullText, searchKeywords)) { continue; } } TreeViewItem* tvi = new TreeViewItem(); tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE; tvi->demod = demod_i; wxTreeItemId itm = m_treeView->AppendItem(activeBranch,activeLabel); SetTreeItemData(itm, tvi); if (nextDemod != nullptr && nextDemod == demod_i) { selItem = itm; wxGetApp().getDemodMgr().setActiveDemodulator(nextDemod, false); nextDemod = nullptr; } else if (!selItem && activeExpandState && lastActiveDemodulator && lastActiveDemodulator == demod_i) { selItem = itm; } } bool rangeExpandState = searchState?false:expandState["range"]; //Ranges const BookmarkRangeList& bmRanges = wxGetApp().getBookmarkMgr().getRanges(); m_treeView->DeleteChildren(rangeBranch); for (auto &re_i: bmRanges) { TreeViewItem* tvi = new TreeViewItem(); tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE; tvi->rangeEnt = re_i; std::wstring labelVal = re_i->label; if (labelVal == L"") { std::string wstr = frequencyToStr(re_i->startFreq) + " - " + frequencyToStr(re_i->endFreq); labelVal = wxString(wstr).ToStdWstring(); } wxTreeItemId itm = m_treeView->AppendItem(rangeBranch, labelVal); SetTreeItemData(itm, tvi); if (nextRange == re_i) { selItem = itm; nextRange = nullptr; } else if (!selItem && rangeExpandState && prevSelCopy && prevSelCopy->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE && prevSelCopy->rangeEnt == re_i) { selItem = itm; } } bool recentExpandState = searchState || expandState["recent"]; // Recents const BookmarkList& bmRecents = wxGetApp().getBookmarkMgr().getRecents(); m_treeView->DeleteChildren(recentBranch); for (auto &bmr_i: bmRecents) { TreeViewItem* tvi = new TreeViewItem(); tvi->type = TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT; tvi->bookmarkEnt = bmr_i; std::wstring labelVal; bmr_i->node->child("user_label")->element()->get(labelVal); if (labelVal == L"") { std::string str = frequencyToStr(bmr_i->frequency) + " " + bmr_i->type; labelVal = wxString(str).ToStdWstring(); } if (searchKeywords.size()) { std::string freqStr = frequencyToStr(bmr_i->frequency); std::string bwStr = frequencyToStr(bmr_i->bandwidth); std::wstring fullText = labelVal + L" " + std::to_wstring(bmr_i->frequency) + L" " + wxString(freqStr).ToStdWstring() + L" " + wxString(bwStr).ToStdWstring() + L" " + wxString(bmr_i->type).ToStdWstring(); if (!isKeywordMatch(fullText, searchKeywords)) { continue; } } wxTreeItemId itm = m_treeView->AppendItem(recentBranch, labelVal); SetTreeItemData(itm, tvi); if (nextEnt == bmr_i) { selItem = itm; nextEnt = nullptr; } else if (!selItem && recentExpandState && prevSelCopy && prevSelCopy->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT && prevSelCopy->bookmarkEnt == bmr_i) { selItem = itm; } } if (activeExpandState) { m_treeView->Expand(activeBranch); } else { m_treeView->Collapse(activeBranch); } if (recentExpandState) { m_treeView->Expand(recentBranch); } else { m_treeView->Collapse(recentBranch); } if (rangeExpandState) { m_treeView->Expand(rangeBranch); } else { m_treeView->Collapse(rangeBranch); } //select the item having the same meaning as the previously selected item if (selItem != nullptr) { m_treeView->SelectItem(selItem); } delete prevSelCopy; } void BookmarkView::onTreeActivate( wxTreeEvent& event ) { wxTreeItemId itm = event.GetItem(); TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(itm)); if (tvi) { if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { if (!tvi->demod->isActive()) { wxGetApp().setFrequency(tvi->demod->getFrequency()); nextDemod = tvi->demod; } } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { nextEnt = tvi->bookmarkEnt; wxGetApp().getBookmarkMgr().removeRecent(tvi->bookmarkEnt); activateBookmark(tvi->bookmarkEnt); } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { activateBookmark(tvi->bookmarkEnt); } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { activateRange(tvi->rangeEnt); } } } void BookmarkView::onTreeCollapse( wxTreeEvent& event ) { bool searchState = (searchKeywords.size() != 0); if (searchState) { event.Skip(); return; } if (event.GetItem() == activeBranch) { expandState["active"] = false; } else if (event.GetItem() == bookmarkBranch) { expandState["bookmark"] = false; } else if (event.GetItem() == recentBranch) { expandState["recent"] = false; } else if (event.GetItem() == rangeBranch) { expandState["range"] = false; } else { TreeViewItem *tvi = itemToTVI(event.GetItem()); if (tvi != nullptr) { if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { wxGetApp().getBookmarkMgr().setExpandState(tvi->groupName,false); } } } } void BookmarkView::onTreeExpanded( wxTreeEvent& event ) { bool searchState = (searchKeywords.size() != 0); if (searchState) { event.Skip(); return; } if (event.GetItem() == activeBranch) { expandState["active"] = true; } else if (event.GetItem() == bookmarkBranch) { expandState["bookmark"] = true; } else if (event.GetItem() == recentBranch) { expandState["recent"] = true; } else if (event.GetItem() == rangeBranch) { expandState["range"] = true; } else { TreeViewItem *tvi = itemToTVI(event.GetItem()); if (tvi != nullptr) { if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { wxGetApp().getBookmarkMgr().setExpandState(tvi->groupName,true); } } } } void BookmarkView::onTreeItemMenu( wxTreeEvent& /* event */ ) { if (m_treeView->GetSelection() == bookmarkBranch) { wxMenu menu; menu.Append(wxCONTEXT_ADD_GROUP_ID, BOOKMARK_VIEW_STR_ADD_GROUP); menu.Connect(wxCONTEXT_ADD_GROUP_ID, wxEVT_MENU, (wxObjectEventFunction)&BookmarkView::onMenuItem, nullptr, this); PopupMenu(&menu); } } void BookmarkView::onMenuItem(wxCommandEvent& event) { if (event.GetId() == wxCONTEXT_ADD_GROUP_ID) { onAddGroup(event); } } bool BookmarkView::isMouseInView() { if (editingLabel) { return true; } if (m_labelText->HasFocus()) { return true; } if (m_searchText->HasFocus()) { return true; } return mouseTracker.mouseInView(); } bool BookmarkView::getExpandState(std::string branchName) { return expandState[branchName]; } void BookmarkView::setExpandState(std::string branchName, bool state) { expandState[branchName] = state; } void BookmarkView::hideProps() { m_frequencyLabel->Hide(); m_frequencyVal->Hide(); m_bandwidthLabel->Hide(); m_bandwidthVal->Hide(); m_modulationVal->Hide(); m_modulationLabel->Hide(); m_labelText->Hide(); m_labelLabel->Hide(); m_propPanel->Hide(); m_buttonPanel->Hide(); } void BookmarkView::showProps() { m_propPanel->Show(); m_propPanel->GetSizer()->Layout(); } void BookmarkView::clearButtons() { m_buttonPanel->Hide(); m_buttonPanel->DestroyChildren(); bookmarkChoice = nullptr; } void BookmarkView::showButtons() { m_buttonPanel->Show(); m_buttonPanel->GetSizer()->Layout(); } void BookmarkView::refreshLayout() { GetSizer()->Layout(); Update(); Refresh(); } wxButton *BookmarkView::makeButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler) { wxButton *nButton = new wxButton( m_buttonPanel, wxID_ANY, labelVal); nButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, handler, nullptr, this); wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); wxColour textColor(ThemeMgr::mgr.currentTheme->text); nButton->SetBackgroundColour(bgColor); nButton->SetForegroundColour(textColor); return nButton; } wxButton *BookmarkView::addButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler) { wxButton *nButton = makeButton(parent, labelVal, handler); parent->GetSizer()->Add( nButton, 0, wxEXPAND); return nButton; } void BookmarkView::doBookmarkActive(std::string group, DemodulatorInstancePtr demod) { wxGetApp().getBookmarkMgr().addBookmark(group, demod); wxGetApp().getBookmarkMgr().updateBookmarks(); } void BookmarkView::doBookmarkRecent(std::string group, BookmarkEntryPtr be) { wxGetApp().getBookmarkMgr().addBookmark(group, be); nextEnt = be; wxGetApp().getBookmarkMgr().removeRecent(be); wxGetApp().getBookmarkMgr().updateBookmarks(); bookmarkSelection(be); } void BookmarkView::doMoveBookmark(BookmarkEntryPtr be, std::string group) { wxGetApp().getBookmarkMgr().moveBookmark(be, group); nextEnt = be; wxGetApp().getBookmarkMgr().updateBookmarks(); bookmarkSelection(be); } void BookmarkView::doRemoveActive(DemodulatorInstancePtr demod) { wxGetApp().getBookmarkMgr().removeActive(demod); wxGetApp().getBookmarkMgr().updateActiveList(); } void BookmarkView::doRemoveRecent(BookmarkEntryPtr be) { wxGetApp().getBookmarkMgr().removeRecent(be); wxGetApp().getBookmarkMgr().updateActiveList(); } void BookmarkView::doClearRecents() { wxGetApp().getBookmarkMgr().clearRecents(); wxGetApp().getBookmarkMgr().updateActiveList(); } void BookmarkView::updateBookmarkChoices() { bookmarkChoices.clear(); TreeViewItem *activeSel = itemToTVI(m_treeView->GetSelection()); bookmarkChoices.push_back(((activeSel != nullptr && activeSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK))?BOOKMARK_VIEW_CHOICE_MOVE:BOOKMARK_VIEW_CHOICE_DEFAULT); wxGetApp().getBookmarkMgr().getGroups(bookmarkChoices); bookmarkChoices.push_back(BOOKMARK_VIEW_CHOICE_NEW); } void BookmarkView::addBookmarkChoice(wxWindow *parent) { updateBookmarkChoices(); bookmarkChoice = new wxChoice(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, bookmarkChoices, wxEXPAND, wxDefaultValidator, "Bookmark"); bookmarkChoice->Select(0); bookmarkChoice->Connect( wxEVT_COMMAND_CHOICE_SELECTED, (wxObjectEventFunction)&BookmarkView::onBookmarkChoice, nullptr, this); parent->GetSizer()->Add(bookmarkChoice, 0, wxALL | wxEXPAND); } void BookmarkView::onBookmarkChoice( wxCommandEvent & /* event */ ) { TreeViewItem *tvi = itemToTVI(m_treeView->GetSelection()); int numSel = bookmarkChoice->GetCount(); int sel = bookmarkChoice->GetSelection(); if (sel == 0) { return; } wxString stringVal = ""; if (sel == (numSel-1)) { stringVal = wxGetTextFromUser(BOOKMARK_VIEW_STR_ADD_GROUP_DESC, BOOKMARK_VIEW_STR_ADD_GROUP, ""); } else { stringVal = bookmarkChoices[sel]; } if (stringVal == "") { return; } if (tvi != nullptr) { if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { doBookmarkActive(stringVal.ToStdString(), tvi->demod); } if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { doBookmarkRecent(stringVal.ToStdString(), tvi->bookmarkEnt); } if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { doMoveBookmark(tvi->bookmarkEnt, stringVal.ToStdString()); } } } void BookmarkView::activeSelection(DemodulatorInstancePtr dsel) { m_frequencyVal->SetLabelText(frequencyToStr(dsel->getFrequency())); m_bandwidthVal->SetLabelText(frequencyToStr(dsel->getBandwidth())); m_modulationVal->SetLabelText(dsel->getDemodulatorType()); m_labelText->SetValue(dsel->getDemodulatorUserLabel()); hideProps(); m_frequencyVal->Show(); m_frequencyLabel->Show(); m_bandwidthVal->Show(); m_bandwidthLabel->Show(); m_modulationVal->Show(); m_modulationLabel->Show(); m_labelText->Show(); m_labelLabel->Show(); clearButtons(); addBookmarkChoice(m_buttonPanel); if (dsel->isActive() && !(dsel->isRecording())) { addButton(m_buttonPanel, "Start Recording", wxCommandEventHandler( BookmarkView::onStartRecording )); } else { addButton(m_buttonPanel, "Stop Recording", wxCommandEventHandler( BookmarkView::onStopRecording )); } addButton(m_buttonPanel, "Remove Active", wxCommandEventHandler( BookmarkView::onRemoveActive )); showProps(); showButtons(); refreshLayout(); } void BookmarkView::activateBookmark(BookmarkEntryPtr bmEnt) { wxTreeItemId selItem = m_treeView->GetSelection(); if (selItem) { m_treeView->SelectItem(selItem, false); } //if a matching DemodulatorInstance do not exist yet, create it and activate it, else use //the already existing one: // we search among the list of existing demodulators the one matching //bmEnt and activate it. The search is made backwards, to select the most recently created one. DemodulatorInstancePtr matchingDemod = wxGetApp().getDemodMgr().getLastDemodulatorWith( bmEnt->type, bmEnt->label, bmEnt->frequency, bmEnt->bandwidth); //not found, create a new demod instance: if (matchingDemod == nullptr) { matchingDemod = wxGetApp().getDemodMgr().loadInstance(bmEnt->node); matchingDemod->run(); wxGetApp().notifyDemodulatorsChanged(); } matchingDemod->setActive(true); long long freq = matchingDemod->getFrequency(); long long currentFreq = wxGetApp().getFrequency(); long long currentRate = wxGetApp().getSampleRate(); if ((abs(freq - currentFreq) > currentRate / 2) || (abs(currentFreq - freq) > currentRate / 2)) { wxGetApp().setFrequency(freq); } nextDemod = matchingDemod; wxGetApp().getBookmarkMgr().updateActiveList(); } void BookmarkView::activateRange(BookmarkRangeEntryPtr rangeEnt) { //the following oly works if rangeEnt->freq is the middle of [rangeEnt->startFreq ; rangeEnt->startFreq] wxGetApp().setFrequency(rangeEnt->freq); // Change View limits to fit the range exactly. wxGetApp().getAppFrame()->setViewState(rangeEnt->startFreq + (rangeEnt->endFreq - rangeEnt->startFreq) / 2, rangeEnt->endFreq - rangeEnt->startFreq); } void BookmarkView::bookmarkSelection(BookmarkEntryPtr bmSel) { m_frequencyVal->SetLabelText(frequencyToStr(bmSel->frequency)); m_bandwidthVal->SetLabelText(frequencyToStr(bmSel->bandwidth)); m_modulationVal->SetLabelText(bmSel->type); m_labelText->SetValue(bmSel->label); hideProps(); m_frequencyVal->Show(); m_frequencyLabel->Show(); m_bandwidthVal->Show(); m_bandwidthLabel->Show(); m_modulationVal->Show(); m_modulationLabel->Show(); m_labelText->Show(); m_labelLabel->Show(); clearButtons(); addBookmarkChoice(m_buttonPanel); addButton(m_buttonPanel, "Activate Bookmark", wxCommandEventHandler( BookmarkView::onActivateBookmark )); addButton(m_buttonPanel, "Remove Bookmark", wxCommandEventHandler( BookmarkView::onRemoveBookmark )); showProps(); showButtons(); refreshLayout(); } void BookmarkView::recentSelection(BookmarkEntryPtr bmSel) { m_frequencyVal->SetLabelText(frequencyToStr(bmSel->frequency)); m_bandwidthVal->SetLabelText(frequencyToStr(bmSel->bandwidth)); m_modulationVal->SetLabelText(bmSel->type); m_labelText->SetValue(bmSel->label); hideProps(); m_frequencyVal->Show(); m_frequencyLabel->Show(); m_bandwidthVal->Show(); m_bandwidthLabel->Show(); m_modulationVal->Show(); m_modulationLabel->Show(); m_labelText->Show(); m_labelLabel->Show(); clearButtons(); addBookmarkChoice(m_buttonPanel); addButton(m_buttonPanel, "Activate Recent", wxCommandEventHandler( BookmarkView::onActivateRecent )); addButton(m_buttonPanel, "Remove Recent", wxCommandEventHandler( BookmarkView::onRemoveRecent )); showProps(); showButtons(); refreshLayout(); } void BookmarkView::groupSelection(std::string groupName) { clearButtons(); hideProps(); m_labelText->SetValue(groupName); m_labelText->Show(); m_labelLabel->Show(); addButton(m_buttonPanel, "Remove Group", wxCommandEventHandler( BookmarkView::onRemoveGroup )); showProps(); showButtons(); refreshLayout(); } void BookmarkView::rangeSelection(BookmarkRangeEntryPtr re) { clearButtons(); hideProps(); m_labelText->SetValue(re->label); m_labelText->Show(); m_labelLabel->Show(); m_frequencyVal->Show(); m_frequencyLabel->Show(); std::string strFreq = frequencyToStr(re->startFreq) + "-" + frequencyToStr(re->endFreq); m_frequencyVal->SetLabelText(wxString(strFreq)); showProps(); addButton(m_buttonPanel, "Go to Range", wxCommandEventHandler( BookmarkView::onActivateRange )); addButton(m_buttonPanel, "Update Range", wxCommandEventHandler( BookmarkView::onUpdateRange ))->SetToolTip("Update range by setting it to the active range."); addButton(m_buttonPanel, "Remove Range", wxCommandEventHandler( BookmarkView::onRemoveRange )); showButtons(); refreshLayout(); } void BookmarkView::bookmarkBranchSelection() { clearButtons(); hideProps(); addButton(m_buttonPanel, BOOKMARK_VIEW_STR_ADD_GROUP, wxCommandEventHandler( BookmarkView::onAddGroup )); showButtons(); refreshLayout(); } void BookmarkView::recentBranchSelection() { clearButtons(); hideProps(); addButton(m_buttonPanel, BOOKMARK_VIEW_STR_CLEAR_RECENT, wxCommandEventHandler( BookmarkView::onClearRecents )); showButtons(); refreshLayout(); this->Layout(); } void BookmarkView::rangeBranchSelection() { clearButtons(); hideProps(); m_labelText->SetValue(wxT("")); m_labelText->Show(); m_labelLabel->Show(); showProps(); addButton(m_buttonPanel, "Add Active Range", wxCommandEventHandler( BookmarkView::onAddRange )); showButtons(); refreshLayout(); this->Layout(); } void BookmarkView::activeBranchSelection() { hideProps(); this->Layout(); } void BookmarkView::onTreeSelect( wxTreeEvent& event ) { wxTreeItemId itm = event.GetItem(); TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(itm)); if (!tvi) { if (itm == bookmarkBranch) { bookmarkBranchSelection(); } else if (itm == activeBranch) { activeBranchSelection(); } else if (itm == recentBranch) { recentBranchSelection(); } else if (itm == rangeBranch) { rangeBranchSelection(); } else { hideProps(); this->Layout(); } return; } if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { activeSelection(tvi->demod); if (tvi->demod->isActive()) { wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, true); wxGetApp().getDemodMgr().setActiveDemodulator(tvi->demod, false); tvi->demod->setTracking(true); } } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { recentSelection(tvi->bookmarkEnt); } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { bookmarkSelection(tvi->bookmarkEnt); } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { groupSelection(tvi->groupName); } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { rangeSelection(tvi->rangeEnt); } else { hideProps(); this->Layout(); } } void BookmarkView::onTreeSelectChanging( wxTreeEvent& event ) { event.Skip(); } void BookmarkView::onLabelText( wxCommandEvent& /* event */ ) { std::wstring newLabel = m_labelText->GetValue().ToStdWstring(); TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel != nullptr) { if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { curSel->demod->setDemodulatorUserLabel(newLabel); wxGetApp().getBookmarkMgr().updateActiveList(); } else if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { curSel->bookmarkEnt->label = m_labelText->GetValue().ToStdWstring(); curSel->bookmarkEnt->node->child("user_label")->element()->set(newLabel); wxGetApp().getBookmarkMgr().updateBookmarks(); } else if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { curSel->bookmarkEnt->label = m_labelText->GetValue().ToStdWstring(); curSel->bookmarkEnt->node->child("user_label")->element()->set(newLabel); wxGetApp().getBookmarkMgr().updateActiveList(); } else if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { curSel->rangeEnt->label = m_labelText->GetValue().ToStdWstring(); wxGetApp().getBookmarkMgr().updateActiveList(); } else if (curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { std::string newGroupName = m_labelText->GetValue().ToStdString(); if (newGroupName != "" && newGroupName != curSel->groupName) { wxGetApp().getBookmarkMgr().renameGroup(curSel->groupName, newGroupName); nextGroup = newGroupName; wxGetApp().getBookmarkMgr().updateBookmarks(); } } } } void BookmarkView::onDoubleClickFreq( wxMouseEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, true); wxGetApp().getDemodMgr().setActiveDemodulator(curSel->demod, false); wxGetApp().showFrequencyInput(FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_DEFAULT); } } void BookmarkView::onDoubleClickBandwidth( wxMouseEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, true); wxGetApp().getDemodMgr().setActiveDemodulator(curSel->demod, false); wxGetApp().showFrequencyInput(FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_BANDWIDTH); } } void BookmarkView::onRemoveActive( wxCommandEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { if (editingLabel) { return; } doRemoveActive(curSel->demod); } } void BookmarkView::onStartRecording( wxCommandEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { if (!curSel->demod->isRecording() && wxGetApp().getConfig()->verifyRecordingPath()) { curSel->demod->setRecording(true); wxGetApp().getBookmarkMgr().updateActiveList(); } } } void BookmarkView::onStopRecording( wxCommandEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { if (curSel->demod->isRecording()) { curSel->demod->setRecording(false); wxGetApp().getBookmarkMgr().updateActiveList(); } } } void BookmarkView::onRemoveBookmark( wxCommandEvent& /* event */ ) { if (editingLabel) { return; } TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { ActionDialog::showDialog(new ActionDialogRemoveBookmark(curSel->bookmarkEnt)); } } void BookmarkView::onActivateBookmark( wxCommandEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { activateBookmark(curSel->bookmarkEnt); } } void BookmarkView::onActivateRecent( wxCommandEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { BookmarkEntryPtr bookmarkEntToActivate = curSel->bookmarkEnt; //let removeRecent() + activateBookmark() refresh the tree //and delete the recent node properly... wxGetApp().getBookmarkMgr().removeRecent(bookmarkEntToActivate); activateBookmark(bookmarkEntToActivate); } } void BookmarkView::onRemoveRecent ( wxCommandEvent& /* event */ ) { if (editingLabel) { return; } TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { BookmarkEntryPtr bookmarkEntToRemove = curSel->bookmarkEnt; //let removeRecent() + updateActiveList() refresh the tree //and delete the recent node properly... wxGetApp().getBookmarkMgr().removeRecent(bookmarkEntToRemove); wxGetApp().getBookmarkMgr().updateActiveList(); } } void BookmarkView::onClearRecents ( wxCommandEvent& /* event */ ) { if (editingLabel) { return; } doClearRecents(); } void BookmarkView::onAddGroup( wxCommandEvent& /* event */ ) { wxString stringVal = wxGetTextFromUser(BOOKMARK_VIEW_STR_ADD_GROUP_DESC, BOOKMARK_VIEW_STR_ADD_GROUP, ""); if (stringVal.ToStdString() != "") { wxGetApp().getBookmarkMgr().addGroup(stringVal.ToStdString()); wxGetApp().getBookmarkMgr().updateBookmarks(); } } void BookmarkView::onRemoveGroup( wxCommandEvent& /* event */ ) { if (editingLabel) { return; } TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { ActionDialog::showDialog(new ActionDialogRemoveGroup(curSel->groupName)); } } void BookmarkView::onAddRange( wxCommandEvent& /* event */ ) { BookmarkRangeEntryPtr re = BookmarkView::makeActiveRangeEntry(); re->label = m_labelText->GetValue(); wxGetApp().getBookmarkMgr().addRange(re); wxGetApp().getBookmarkMgr().updateActiveList(); } void BookmarkView::onRemoveRange( wxCommandEvent& /* event */ ) { if (editingLabel) { return; } TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { ActionDialog::showDialog(new ActionDialogRemoveRange(curSel->rangeEnt)); } } void BookmarkView::onRenameRange( wxCommandEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (!curSel || curSel->type != TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { return; } wxString stringVal = ""; stringVal = wxGetTextFromUser(BOOKMARK_VIEW_STR_RENAME_GROUP, "New Group Name", curSel->groupName); std::string newGroupName = stringVal.Trim().ToStdString(); if (newGroupName != "") { wxGetApp().getBookmarkMgr().renameGroup(curSel->groupName, newGroupName); wxGetApp().getBookmarkMgr().updateBookmarks(); } } void BookmarkView::onActivateRange( wxCommandEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { activateRange(curSel->rangeEnt); } } void BookmarkView::onUpdateRange( wxCommandEvent& /* event */ ) { TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection()); if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RANGE) { ActionDialog::showDialog(new ActionDialogUpdateRange(curSel->rangeEnt)); } } void BookmarkView::onTreeBeginDrag( wxTreeEvent& event ) { TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(event.GetItem())); dragItem = nullptr; dragItemId = nullptr; SetCursor(wxCURSOR_CROSS); if (!tvi) { event.Veto(); return; } bool bAllow = false; std::wstring dragItemName; if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { bAllow = true; dragItemName = BookmarkMgr::getActiveDisplayName(tvi->demod); } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT || tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { bAllow = true; dragItemName = BookmarkMgr::getBookmarkEntryDisplayName(tvi->bookmarkEnt); } if (bAllow) { wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); wxColour textColor(ThemeMgr::mgr.currentTheme->text); m_treeView->SetBackgroundColour(textColor); m_treeView->SetForegroundColour(bgColor); // m_treeView->SetToolTip("Dragging " + dragItemName); dragItem = tvi; dragItemId = event.GetItem(); visualDragItem = new BookmarkViewVisualDragItem(dragItemName); event.Allow(); } else { event.Veto(); } } void BookmarkView::onTreeEndDrag( wxTreeEvent& event ) { wxColour bgColor(ThemeMgr::mgr.currentTheme->generalBackground); wxColour textColor(ThemeMgr::mgr.currentTheme->text); m_treeView->SetBackgroundColour(bgColor); m_treeView->SetForegroundColour(textColor); m_treeView->UnsetToolTip(); SetCursor(wxCURSOR_ARROW); if (visualDragItem != nullptr) { visualDragItem->Destroy(); delete visualDragItem; visualDragItem = nullptr; } if (!event.GetItem()) { event.Veto(); return; } TreeViewItem* tvi = dynamic_cast(m_treeView->GetItemData(event.GetItem())); if (!tvi) { if (event.GetItem() == bookmarkBranch) { if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { doBookmarkActive(BOOKMARK_VIEW_STR_UNNAMED, dragItem->demod); } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { doBookmarkRecent(BOOKMARK_VIEW_STR_UNNAMED, dragItem->bookmarkEnt); } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { doMoveBookmark(dragItem->bookmarkEnt, BOOKMARK_VIEW_STR_UNNAMED); } } return; } if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_GROUP) { if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { // Active -> Group Item doBookmarkActive(tvi->groupName, dragItem->demod); } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { // Recent -> Group Item doBookmarkRecent(tvi->groupName, dragItem->bookmarkEnt); m_treeView->Delete(dragItemId); } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { // Bookmark -> Group Item doMoveBookmark(dragItem->bookmarkEnt, tvi->groupName); } } else if (tvi->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) { // Active -> Same Group doBookmarkActive(tvi->groupName, dragItem->demod); } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_RECENT) { // Recent -> Same Group doBookmarkRecent(tvi->groupName, dragItem->bookmarkEnt); m_treeView->Delete(dragItemId); } else if (dragItem && dragItem->type == TreeViewItem::TREEVIEW_ITEM_TYPE_BOOKMARK) { // Bookmark -> Same Group doMoveBookmark(dragItem->bookmarkEnt, tvi->groupName); } } } void BookmarkView::onTreeItemGetTooltip( wxTreeEvent& event ) { event.Skip(); } void BookmarkView::onEnterWindow( wxMouseEvent& event ) { mouseTracker.OnMouseEnterWindow(event); #ifdef _WIN32 if (wxGetApp().getAppFrame()->canFocus()) { //make mousewheel work in the tree view. m_treeView->SetFocus(); } #endif setStatusText("Drag & Drop to create / move bookmarks, Group and arrange bookmarks, quick Search by keywords."); } void BookmarkView::onLeaveWindow( wxMouseEvent& event ) { mouseTracker.OnMouseLeftWindow(event); } void BookmarkView::onMotion( wxMouseEvent& event ) { mouseTracker.OnMouseMoved(event); wxPoint pos = ClientToScreen(event.GetPosition()); pos += wxPoint(30,-10); if (visualDragItem != nullptr) { visualDragItem->SetPosition(pos); } event.Skip(); } void BookmarkView::setStatusText(std::string statusText) { //make tooltips active on the tree view. wxGetApp().getAppFrame()->setStatusText(m_treeView, statusText); } TreeViewItem *BookmarkView::itemToTVI(wxTreeItemId item) { TreeViewItem* tvi = nullptr; if (item != nullptr) { tvi = dynamic_cast(m_treeView->GetItemData(item)); } return tvi; } void BookmarkView::onSearchTextFocus( wxMouseEvent& event ) { mouseTracker.OnMouseMoved(event); //apparently needed ??? m_searchText->SetFocus(); if (m_searchText->GetValue() == L"Search..") { //select the whole field, so that typing //replaces the whole text by the new one right away. m_searchText->SetSelection(-1, -1); } else if (!m_searchText->GetValue().Trim().empty()) { //position at the end of the existing field, so we can append //or truncate the existing field. m_searchText->SetInsertionPointEnd(); } else { //empty field, restore displaying L"Search.." m_searchText->SetValue(L"Search.."); m_searchText->SetSelection(-1, -1); } } void BookmarkView::onSearchText( wxCommandEvent& event ) { std::wstring searchText = m_searchText->GetValue().Trim().Lower().ToStdWstring(); searchKeywords.clear(); if (searchText.length() != 0) { std::wstringstream searchTextLo(searchText); std::wstring tmp; while(std::getline(searchTextLo, tmp, L' ')) { if (tmp.length() != 0 && tmp.find(L"search.") == std::wstring::npos) { searchKeywords.push_back(tmp); // std::wcout << L"Keyword: " << tmp << '\n'; } } } if (searchKeywords.size() != 0 && !m_clearSearchButton->IsShown()) { m_clearSearchButton->Show(); refreshLayout(); } else if (searchKeywords.size() == 0 && m_clearSearchButton->IsShown()) { m_clearSearchButton->Hide(); refreshLayout(); } wxGetApp().getBookmarkMgr().updateActiveList(); wxGetApp().getBookmarkMgr().updateBookmarks(); } void BookmarkView::onClearSearch( wxCommandEvent& /* event */ ) { m_clearSearchButton->Hide(); m_searchText->SetValue(L"Search.."); m_treeView->SetFocus(); searchKeywords.clear(); wxGetApp().getBookmarkMgr().updateActiveList(); wxGetApp().getBookmarkMgr().updateBookmarks(); refreshLayout(); } void BookmarkView::loadDefaultRanges() { wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"160 Meters", 1900000, 1800000, 2000000)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"80 Meters", 3750000, 3500000, 4000000)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"60 Meters", 5368500, 5332000, 5405000)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"40 Meters", 7150000, 7000000, 7300000)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"30 Meters", 10125000, 10100000, 10150000)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"20 Meters", 14175000, 14000000, 14350000)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"17 Meters", 18068180, 17044180, 19092180)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"15 Meters", 21225000, 21000000, 21450000)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"12 Meters", 24940000, 24890000, 24990000)); wxGetApp().getBookmarkMgr().addRange(std::make_shared(L"10 Meters", 28850000, 28000000, 29700000)); } BookmarkRangeEntryPtr BookmarkView::makeActiveRangeEntry() { BookmarkRangeEntryPtr re(new BookmarkRangeEntry); re->startFreq = wxGetApp().getAppFrame()->getViewCenterFreq() - (wxGetApp().getAppFrame()->getViewBandwidth()/2); re->endFreq = wxGetApp().getAppFrame()->getViewCenterFreq() + (wxGetApp().getAppFrame()->getViewBandwidth()/2); //to prevent problems, always make the re->freq the middle of the interval. re->freq = (re->startFreq + re->endFreq) / 2; return re; } void BookmarkView::SetTreeItemData(const wxTreeItemId& item, wxTreeItemData *data) { TreeViewItem *itemData = itemToTVI(item); if (itemData != NULL) { //cleanup previous data, if any delete itemData; } m_treeView->SetItemData(item, data); } CubicSDR-0.2.3/src/forms/Bookmark/BookmarkView.h000066400000000000000000000142741322677621400213670ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/choice.h" #include "wx/dialog.h" #include "BookmarkPanel.h" #include "BookmarkMgr.h" #include "MouseTracker.h" class TreeViewItem : public wxTreeItemData { public: enum TreeViewItemType { TREEVIEW_ITEM_TYPE_GROUP, TREEVIEW_ITEM_TYPE_ACTIVE, TREEVIEW_ITEM_TYPE_RECENT, TREEVIEW_ITEM_TYPE_BOOKMARK, TREEVIEW_ITEM_TYPE_RANGE }; TreeViewItem() { demod = nullptr; bookmarkEnt = nullptr; rangeEnt = nullptr; }; // copy constructor TreeViewItem(const TreeViewItem& src) { demod = src.demod; bookmarkEnt = src.bookmarkEnt; rangeEnt = src.rangeEnt; type = src.type; groupName = src.groupName; }; virtual ~TreeViewItem() { // }; TreeViewItemType type; BookmarkEntryPtr bookmarkEnt; BookmarkRangeEntryPtr rangeEnt; DemodulatorInstancePtr demod; std::string groupName; }; class BookmarkViewVisualDragItem : public wxDialog { public: BookmarkViewVisualDragItem(wxString labelValue = L"Popup"); }; class BookmarkView : public BookmarkPanel { public: BookmarkView( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxTAB_TRAVERSAL ); virtual ~BookmarkView(); //order an asynchronous refresh/rebuild of the whole tree, //will take effect at the next onUpdateTimer() occurence. void updateActiveList(); //order asynchronous updates of the bookmarks, //will take effect at the next onUpdateTimer() occurence. void updateBookmarks(); void updateBookmarks(std::string group); bool isKeywordMatch(std::wstring str, std::vector &keywords); wxTreeItemId refreshBookmarks(); void updateTheme(); void onMenuItem(wxCommandEvent& event); bool isMouseInView(); bool getExpandState(std::string branchName); void setExpandState(std::string branchName, bool state); void loadDefaultRanges(); static BookmarkRangeEntryPtr makeActiveRangeEntry(); protected: void activeSelection(DemodulatorInstancePtr dsel); void bookmarkSelection(BookmarkEntryPtr bmSel); void rangeSelection(BookmarkRangeEntryPtr re); void activateBookmark(BookmarkEntryPtr bmEnt); void activateRange(BookmarkRangeEntryPtr rangeEnt); void recentSelection(BookmarkEntryPtr bmSel); void groupSelection(std::string groupName); void bookmarkBranchSelection(); void recentBranchSelection(); void rangeBranchSelection(); void activeBranchSelection(); void hideProps(); void showProps(); void onUpdateTimer( wxTimerEvent& event ); //refresh / rebuild the whole tree item immediatly void doUpdateActiveList(); void onTreeActivate( wxTreeEvent& event ); void onTreeCollapse( wxTreeEvent& event ); void onTreeExpanded( wxTreeEvent& event ); void onTreeItemMenu( wxTreeEvent& event ); void onTreeSelect( wxTreeEvent& event ); void onTreeSelectChanging( wxTreeEvent& event ); void onLabelText( wxCommandEvent& event ); void onDoubleClickFreq( wxMouseEvent& event ); void onDoubleClickBandwidth( wxMouseEvent& event ); void onTreeBeginDrag( wxTreeEvent& event ); void onTreeEndDrag( wxTreeEvent& event ); void onTreeItemGetTooltip( wxTreeEvent& event ); void onEnterWindow( wxMouseEvent& event ); void onLeaveWindow( wxMouseEvent& event ); void onMotion( wxMouseEvent& event ); void onSearchTextFocus( wxMouseEvent& event ); void onSearchText( wxCommandEvent& event ); void onClearSearch( wxCommandEvent& event ); void clearButtons(); void showButtons(); void refreshLayout(); wxButton *makeButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler); wxButton *addButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler); void doBookmarkActive(std::string group, DemodulatorInstancePtr demod); void doBookmarkRecent(std::string group, BookmarkEntryPtr be); void doMoveBookmark(BookmarkEntryPtr be, std::string group); void doRemoveActive(DemodulatorInstancePtr demod); void doRemoveRecent(BookmarkEntryPtr be); void doClearRecents(); void updateBookmarkChoices(); void addBookmarkChoice(wxWindow *parent); void onBookmarkChoice( wxCommandEvent &event ); void onRemoveActive( wxCommandEvent& event ); void onStartRecording( wxCommandEvent& event ); void onStopRecording( wxCommandEvent& event ); void onRemoveBookmark( wxCommandEvent& event ); void onActivateBookmark( wxCommandEvent& event ); void onActivateRecent( wxCommandEvent& event ); void onRemoveRecent ( wxCommandEvent& event ); void onClearRecents ( wxCommandEvent& event ); void onAddGroup( wxCommandEvent& event ); void onRemoveGroup( wxCommandEvent& event ); void onAddRange( wxCommandEvent& event ); void onRemoveRange( wxCommandEvent& event ); void onRenameRange( wxCommandEvent& event ); void onActivateRange( wxCommandEvent& event ); void onUpdateRange( wxCommandEvent& event ); TreeViewItem *itemToTVI(wxTreeItemId item); void SetTreeItemData(const wxTreeItemId& item, wxTreeItemData *data); MouseTracker mouseTracker; wxTreeItemId rootBranch, activeBranch, bookmarkBranch, recentBranch, rangeBranch; std::map expandState; TreeViewItem *dragItem; wxTreeItemId dragItemId; BookmarkViewVisualDragItem *visualDragItem; bool editingLabel; // Bookmarks std::atomic_bool doUpdateBookmarks; std::set< std::string > doUpdateBookmarkGroup; BookmarkNames groupNames; std::map groups; wxArrayString bookmarkChoices; wxChoice *bookmarkChoice; // Active std::atomic_bool doUpdateActive; // Focus BookmarkEntryPtr nextEnt; BookmarkRangeEntryPtr nextRange; DemodulatorInstancePtr nextDemod; std::string nextGroup; // Search std::vector searchKeywords; void setStatusText(std::string statusText); }; CubicSDR-0.2.3/src/forms/Dialog/000077500000000000000000000000001322677621400162405ustar00rootroot00000000000000CubicSDR-0.2.3/src/forms/Dialog/AboutDialog.cpp000066400000000000000000000005561322677621400211440ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "AboutDialog.h" AboutDialog::AboutDialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : AboutDialogBase(parent, id, title, pos, size, style) { m_appName->SetLabelText(CUBICSDR_INSTALL_NAME " v" CUBICSDR_VERSION); } CubicSDR-0.2.3/src/forms/Dialog/AboutDialog.fbp000066400000000000000000027103251322677621400211350ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect AboutDialogBase 1000 none 0 AboutDialogBase . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT wxBOTH 1 1 impl_virtual 0 wxID_ANY AboutDialogBase 530,420 wxDEFAULT_DIALOG_STYLE About dlgSizer wxVERTICAL none 5 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_hPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL m_hSizer wxHORIZONTAL none 6 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,20,70,0 0 0 wxID_ANY CubicSDR 0 0 1 m_appName 1 protected 1 Resizable 1 0 -1 5 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_aboutNotebook 1 protected 1 Resizable 1 0 Developers 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_dbScroll 1 protected 1 Resizable 5 5 1 0 wxHSCROLL|wxVSCROLL m_dbPane wxVERTICAL none 5 wxALL|wxEXPAND 0 3 wxBOTH 20 m_dbSizer wxFLEX_GROWMODE_ALL none 0 2 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,15,70,0 0 0 wxID_ANY Developed By 0 0 1 m_dbHeader 1 protected 1 Resizable 1 wxST_NO_AUTORESIZE 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,-1,70,0 0 0 wxID_ANY GitHub 0 0 1 m_dbGHHeader 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,-1,70,0 0 0 wxID_ANY Twitter 0 0 1 m_dbTwitter 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Charles J. Cliffe 0 0 1 m_dbCharlesCliffe 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @cjcliffe 0 0 1 m_dbghCC 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @ccliffe 0 0 1 m_dbtCC 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Vincent Sonnier 0 0 1 m_dbVincentSonnier 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @vsonnier 0 0 1 m_dbghVS 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @VincentSonnier 0 0 1 m_dbtVS 1 protected 1 Resizable 1 0 -1 10 wxALL|wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_dbDivider1 1 protected 1 Resizable 1 wxLI_HORIZONTAL 0 5 wxALL|wxEXPAND 0 2 wxBOTH 20 m_cSizer wxFLEX_GROWMODE_ALL none 0 2 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,15,70,0 0 0 wxID_ANY Contributors 0 0 1 m_cContributorsHeader 1 protected 1 Resizable 1 wxST_NO_AUTORESIZE 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,-1,70,0 0 0 wxID_ANY GitHub 0 0 1 m_cGitHub 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Corne Lukken 0 0 1 m_cCorneLukken 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @Dantali0n 0 0 1 m_cghCL 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY StanisÅ‚aw Pitucha 0 0 1 m_cStainislawPitucha 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @viraptor 0 0 1 m_cghSP 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Ștefan Talpalaru 0 0 1 m_cghStefanTalpalaru 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @stefantalpalaru 0 0 1 m_cghST 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Chris Motch 0 0 1 m_cCrisMotch 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @bodrick 0 0 1 m_cghCM 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Mariusz Ryndzionek 0 0 1 m_cMariuszRyndzionek 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @mryndzionek 0 0 1 m_cghMR 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Jiang Wei 0 0 1 m_cJiangWei 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @jocover 0 0 1 m_cghJW 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Tom Swartz 0 0 1 m_cTomSwartz 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @tomswartz07 0 0 1 m_cghTS 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Infinity Cyberworks 0 0 1 m_cInfinityCyberworks 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY @infinitycyberworks 0 0 1 m_cghIC 1 protected 1 Resizable 1 0 -1 Donations 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_dScroll 1 protected 1 Resizable 5 5 1 0 wxHSCROLL|wxVSCROLL m_dBSizer wxVERTICAL none 5 wxALL|wxEXPAND 1 m_dSizer wxVERTICAL none 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,15,70,0 0 0 wxID_ANY Thanks to everyone who donated at cubicsdr.com! 0 0 1 m_dHeader 1 protected 1 Resizable 1 wxST_NO_AUTORESIZE 0 -1 5 wxEXPAND | wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_dDivider1 1 protected 1 Resizable 1 wxLI_HORIZONTAL 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY SDRplay / sdrplay.com 0 0 1 m_dSDRplay 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Michael Ladd 0 0 1 m_dMichaelLadd 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Automotive Templates 0 0 1 m_dAutoMotiveTemplates 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Jorge Morales 0 0 1 m_dJorgeMorales 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Michael Rooke 0 0 1 m_dMichaelRooke 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY TNCOM 0 0 1 m_dTNCOM 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Erik Mikkel Wied 0 0 1 m_dErikWied 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Robert Duering 0 0 1 m_dRobertDuering 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Jim Deitch 0 0 1 m_dJimDeitch 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY NooElec Inc. / nooelec.com 0 0 1 m_dNooElec 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY David Ahlgren 0 0 1 m_dDavidAhlgren 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Ronald Cook 0 0 1 m_dRonaldCook 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Eric Peterson 0 0 1 m_dEricPeterson 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Geo Distributing 0 0 1 m_dGeoDistributing 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY James Carson 0 0 1 m_dJamesCarson 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Craig Williams 0 0 1 m_dCraigWilliams 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Rudolf Schaffer 0 0 1 m_dRudolfShaffer 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY John Katon 0 0 1 m_dJohnKaton 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Vincent Sonnier 0 0 1 m_dVincentSonnier 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY corq's auctions/L. Easterly LTD (x 4) 0 0 1 m_dCorq 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Ivan Alekseev 0 0 1 m_dIvanAlekseev 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Ole-Jørgen Næss Kolsrud 0 0 1 m_dOleJorgenKolsrud 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Henrik Jagemyr 0 0 1 m_dHenrikJagemyr 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Peter Haines 0 0 1 m_dPeterHaines 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Leon Abrassart 0 0 1 m_dLeonAbrassart 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY George Alan Talbot 0 0 1 m_dGeorgeTalbot 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Francisco Borja Marcos de la Puerta 0 0 1 m_dFranciscoPuerta 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Ronald A. Lundeen 0 0 1 m_dRonaldLundeen 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Walter Horbert 0 0 1 m_dWalterHorbert 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY William Lloyd-Davies 0 0 1 m_dWilliamLD 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Bratislav Arandjelovic 0 0 1 m_dBratislavArandjelovic 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Gary Martin 0 0 1 m_dGaryMartin 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Einars Repse 0 0 1 m_dEinarsRepse 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Timothy Gatton 0 0 1 m_dTimothyGatton 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Stephen Cuccio 0 0 1 m_dStephenCuccio 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Keshavlal Patel 0 0 1 m_dKeshavlalPatel 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Bob Schatzman 0 0 1 m_dBobSchatzman 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Robert Ross 0 0 1 m_dRobertRoss 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Roberto Bellotti 0 0 1 m_dRobertoBellotti 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Serge Van der Torre 0 0 1 m_dSergeVanderTorre 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Dieter Schneider 0 0 1 m_dDieterSchneider 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Petrika Janeku 0 0 1 m_dPetrikaJaneku 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Chad Myslinsky 0 0 1 m_dChadMyslinsky 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Charlie Bruckner 0 0 1 m_dCharlieBruckner 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Jordan Parker 0 0 1 m_dJordanParker 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Robert Chave 0 0 1 m_dRobertChave 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Marvin Calvert 0 0 1 m_dMarvinCalvert 1 protected 1 Resizable 1 ; forward_declare 0 -1 Special Thanks 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_stScroll 1 protected 1 Resizable 5 5 1 0 wxHSCROLL|wxVSCROLL m_stBSizer wxVERTICAL none 5 wxALL|wxEXPAND 1 m_stSizer wxVERTICAL none 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,15,70,0 0 0 wxID_ANY Special Thanks To 0 0 1 m_stHeader 1 protected 1 Resizable 1 wxST_NO_AUTORESIZE 0 -1 5 wxEXPAND | wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_stDivider1 1 protected 1 Resizable 1 wxLI_HORIZONTAL 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,92,10,70,0 0 0 wxID_ANY SoapySDR Development and Assistance: 0 0 1 m_stSoapyDevAssistHeader 1 protected 1 Resizable 1 wxST_NO_AUTORESIZE 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Josh Blum / @guruofquality / pothosware.com 0 0 1 m_stJoshBlum 1 protected 1 Resizable 1 0 -1 5 wxEXPAND | wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_stDivider2 1 protected 1 Resizable 1 wxLI_HORIZONTAL 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,92,10,70,0 0 0 wxID_ANY Liquid-DSP Development and Assistance: 0 0 1 m_stLiquidDSPHeader 1 protected 1 Resizable 1 wxST_NO_AUTORESIZE 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Joseph D. Gaeddert / @jgaeddert / liquidsdr.com 0 0 1 m_stJosephGaeddert 1 protected 1 Resizable 1 0 -1 5 wxEXPAND | wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_stDivider3 1 protected 1 Resizable 1 wxLI_HORIZONTAL 0 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,92,10,70,0 0 0 wxID_ANY Ideas, Direction && Encouragement: 0 0 1 m_stIdeasDirectionsHeader 1 protected 1 Resizable 1 wxST_NO_AUTORESIZE 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Ton Machielsen / @Toontje / @EA3HOE 0 0 1 m_stTonMachielsen 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Mike Ladd / KD2KOG.com 0 0 1 m_stMikeLadd 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY SDRplay team / @SDRplay / SDRplay.com 0 0 1 m_stSDRplay 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY SDRplay Facebook group 0 0 1 m_stSDRplayFB 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Paul Warren / @pwarren 0 0 1 m_stPaulWarren 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Segesdi Károly / @jazzkutya 0 0 1 m_stSegesdiKaroly 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Reddit RTL-SDR group /r/rtlsdr 0 0 1 m_stRedditRTLSDR 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY NooElec team / NooElec.com 0 0 1 m_stNooElec 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Everyone who's contributed to the GitHub issues; thanks! 0 0 1 m_stGHIssues 1 protected 1 Resizable 1 0 -1 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Please feel free to nominate anyone we might have missed. 0 0 1 m_stNominate 1 protected 1 Resizable 1 0 -1 CubicSDR-0.2.3/src/forms/Dialog/AboutDialog.h000066400000000000000000000007151322677621400206060ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "AboutDialogBase.h" #include "CubicSDRDefs.h" class AboutDialog : public AboutDialogBase { public: AboutDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("About"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 530, 420 ), long style = wxDEFAULT_DIALOG_STYLE ); }; CubicSDR-0.2.3/src/forms/Dialog/AboutDialogBase.cpp000066400000000000000000000571421322677621400217420ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Nov 6 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "AboutDialogBase.h" /////////////////////////////////////////////////////////////////////////// AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { this->SetSizeHints( wxDefaultSize, wxDefaultSize ); wxBoxSizer* dlgSizer; dlgSizer = new wxBoxSizer( wxVERTICAL ); m_hPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* m_hSizer; m_hSizer = new wxBoxSizer( wxHORIZONTAL ); m_appName = new wxStaticText( m_hPanel, wxID_ANY, wxT("CubicSDR"), wxDefaultPosition, wxDefaultSize, 0 ); m_appName->Wrap( -1 ); m_appName->SetFont( wxFont( 20, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); m_hSizer->Add( m_appName, 0, wxALL, 6 ); m_hPanel->SetSizer( m_hSizer ); m_hPanel->Layout(); m_hSizer->Fit( m_hPanel ); dlgSizer->Add( m_hPanel, 0, wxALL|wxEXPAND, 5 ); m_aboutNotebook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); m_dbScroll = new wxScrolledWindow( m_aboutNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); m_dbScroll->SetScrollRate( 5, 5 ); wxBoxSizer* m_dbPane; m_dbPane = new wxBoxSizer( wxVERTICAL ); wxFlexGridSizer* m_dbSizer; m_dbSizer = new wxFlexGridSizer( 0, 3, 2, 20 ); m_dbSizer->SetFlexibleDirection( wxBOTH ); m_dbSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL ); m_dbHeader = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Developed By"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE ); m_dbHeader->Wrap( -1 ); m_dbHeader->SetFont( wxFont( 15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); m_dbSizer->Add( m_dbHeader, 0, wxALL, 5 ); m_dbGHHeader = new wxStaticText( m_dbScroll, wxID_ANY, wxT("GitHub"), wxDefaultPosition, wxDefaultSize, 0 ); m_dbGHHeader->Wrap( -1 ); m_dbGHHeader->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); m_dbSizer->Add( m_dbGHHeader, 0, wxALL, 5 ); m_dbTwitter = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Twitter"), wxDefaultPosition, wxDefaultSize, 0 ); m_dbTwitter->Wrap( -1 ); m_dbTwitter->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); m_dbSizer->Add( m_dbTwitter, 0, wxALL, 5 ); m_dbCharlesCliffe = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Charles J. Cliffe"), wxDefaultPosition, wxDefaultSize, 0 ); m_dbCharlesCliffe->Wrap( -1 ); m_dbSizer->Add( m_dbCharlesCliffe, 0, wxALL, 5 ); m_dbghCC = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@cjcliffe"), wxDefaultPosition, wxDefaultSize, 0 ); m_dbghCC->Wrap( -1 ); m_dbSizer->Add( m_dbghCC, 0, wxALL, 5 ); m_dbtCC = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@ccliffe"), wxDefaultPosition, wxDefaultSize, 0 ); m_dbtCC->Wrap( -1 ); m_dbSizer->Add( m_dbtCC, 0, wxALL, 5 ); m_dbVincentSonnier = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Vincent Sonnier"), wxDefaultPosition, wxDefaultSize, 0 ); m_dbVincentSonnier->Wrap( -1 ); m_dbSizer->Add( m_dbVincentSonnier, 0, wxALL, 5 ); m_dbghVS = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@vsonnier"), wxDefaultPosition, wxDefaultSize, 0 ); m_dbghVS->Wrap( -1 ); m_dbSizer->Add( m_dbghVS, 0, wxALL, 5 ); m_dbtVS = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@VincentSonnier"), wxDefaultPosition, wxDefaultSize, 0 ); m_dbtVS->Wrap( -1 ); m_dbSizer->Add( m_dbtVS, 0, wxALL, 5 ); m_dbPane->Add( m_dbSizer, 0, wxALL|wxEXPAND, 5 ); m_dbDivider1 = new wxStaticLine( m_dbScroll, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); m_dbPane->Add( m_dbDivider1, 0, wxALL|wxEXPAND, 10 ); wxFlexGridSizer* m_cSizer; m_cSizer = new wxFlexGridSizer( 0, 2, 2, 20 ); m_cSizer->SetFlexibleDirection( wxBOTH ); m_cSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL ); m_cContributorsHeader = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Contributors"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE ); m_cContributorsHeader->Wrap( -1 ); m_cContributorsHeader->SetFont( wxFont( 15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); m_cSizer->Add( m_cContributorsHeader, 0, wxALL, 5 ); m_cGitHub = new wxStaticText( m_dbScroll, wxID_ANY, wxT("GitHub"), wxDefaultPosition, wxDefaultSize, 0 ); m_cGitHub->Wrap( -1 ); m_cGitHub->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); m_cSizer->Add( m_cGitHub, 0, wxALL, 5 ); m_cCorneLukken = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Corne Lukken"), wxDefaultPosition, wxDefaultSize, 0 ); m_cCorneLukken->Wrap( -1 ); m_cSizer->Add( m_cCorneLukken, 0, wxALL, 5 ); m_cghCL = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@Dantali0n"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghCL->Wrap( -1 ); m_cSizer->Add( m_cghCL, 0, wxALL, 5 ); m_cStainislawPitucha = new wxStaticText( m_dbScroll, wxID_ANY, wxT("StanisÅ‚aw Pitucha"), wxDefaultPosition, wxDefaultSize, 0 ); m_cStainislawPitucha->Wrap( -1 ); m_cSizer->Add( m_cStainislawPitucha, 0, wxALL, 5 ); m_cghSP = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@viraptor"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghSP->Wrap( -1 ); m_cSizer->Add( m_cghSP, 0, wxALL, 5 ); m_cghStefanTalpalaru = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Ștefan Talpalaru"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghStefanTalpalaru->Wrap( -1 ); m_cSizer->Add( m_cghStefanTalpalaru, 0, wxALL, 5 ); m_cghST = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@stefantalpalaru"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghST->Wrap( -1 ); m_cSizer->Add( m_cghST, 0, wxALL, 5 ); m_cCrisMotch = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Chris Motch"), wxDefaultPosition, wxDefaultSize, 0 ); m_cCrisMotch->Wrap( -1 ); m_cSizer->Add( m_cCrisMotch, 0, wxALL, 5 ); m_cghCM = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@bodrick"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghCM->Wrap( -1 ); m_cSizer->Add( m_cghCM, 0, wxALL, 5 ); m_cMariuszRyndzionek = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Mariusz Ryndzionek"), wxDefaultPosition, wxDefaultSize, 0 ); m_cMariuszRyndzionek->Wrap( -1 ); m_cSizer->Add( m_cMariuszRyndzionek, 0, wxALL, 5 ); m_cghMR = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@mryndzionek"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghMR->Wrap( -1 ); m_cSizer->Add( m_cghMR, 0, wxALL, 5 ); m_cJiangWei = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Jiang Wei"), wxDefaultPosition, wxDefaultSize, 0 ); m_cJiangWei->Wrap( -1 ); m_cSizer->Add( m_cJiangWei, 0, wxALL, 5 ); m_cghJW = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@jocover"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghJW->Wrap( -1 ); m_cSizer->Add( m_cghJW, 0, wxALL, 5 ); m_cTomSwartz = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Tom Swartz"), wxDefaultPosition, wxDefaultSize, 0 ); m_cTomSwartz->Wrap( -1 ); m_cSizer->Add( m_cTomSwartz, 0, wxALL, 5 ); m_cghTS = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@tomswartz07"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghTS->Wrap( -1 ); m_cSizer->Add( m_cghTS, 0, wxALL, 5 ); m_cInfinityCyberworks = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Infinity Cyberworks"), wxDefaultPosition, wxDefaultSize, 0 ); m_cInfinityCyberworks->Wrap( -1 ); m_cSizer->Add( m_cInfinityCyberworks, 0, wxALL, 5 ); m_cghIC = new wxStaticText( m_dbScroll, wxID_ANY, wxT("@infinitycyberworks"), wxDefaultPosition, wxDefaultSize, 0 ); m_cghIC->Wrap( -1 ); m_cSizer->Add( m_cghIC, 0, wxALL, 5 ); m_dbPane->Add( m_cSizer, 0, wxALL|wxEXPAND, 5 ); m_dbScroll->SetSizer( m_dbPane ); m_dbScroll->Layout(); m_dbPane->Fit( m_dbScroll ); m_aboutNotebook->AddPage( m_dbScroll, wxT("Developers"), false ); m_dScroll = new wxScrolledWindow( m_aboutNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); m_dScroll->SetScrollRate( 5, 5 ); wxBoxSizer* m_dBSizer; m_dBSizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* m_dSizer; m_dSizer = new wxBoxSizer( wxVERTICAL ); m_dHeader = new wxStaticText( m_dScroll, wxID_ANY, wxT("Thanks to everyone who donated at cubicsdr.com!"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE ); m_dHeader->Wrap( -1 ); m_dHeader->SetFont( wxFont( 15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); m_dSizer->Add( m_dHeader, 0, wxALL, 5 ); m_dDivider1 = new wxStaticLine( m_dScroll, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); m_dSizer->Add( m_dDivider1, 0, wxEXPAND | wxALL, 5 ); m_dSDRplay = new wxStaticText( m_dScroll, wxID_ANY, wxT("SDRplay / sdrplay.com"), wxDefaultPosition, wxDefaultSize, 0 ); m_dSDRplay->Wrap( -1 ); m_dSizer->Add( m_dSDRplay, 0, wxALL, 5 ); m_dMichaelLadd = new wxStaticText( m_dScroll, wxID_ANY, wxT("Michael Ladd"), wxDefaultPosition, wxDefaultSize, 0 ); m_dMichaelLadd->Wrap( -1 ); m_dSizer->Add( m_dMichaelLadd, 0, wxALL, 5 ); m_dAutoMotiveTemplates = new wxStaticText( m_dScroll, wxID_ANY, wxT("Automotive Templates"), wxDefaultPosition, wxDefaultSize, 0 ); m_dAutoMotiveTemplates->Wrap( -1 ); m_dSizer->Add( m_dAutoMotiveTemplates, 0, wxALL, 5 ); m_dJorgeMorales = new wxStaticText( m_dScroll, wxID_ANY, wxT("Jorge Morales"), wxDefaultPosition, wxDefaultSize, 0 ); m_dJorgeMorales->Wrap( -1 ); m_dSizer->Add( m_dJorgeMorales, 0, wxALL, 5 ); m_dMichaelRooke = new wxStaticText( m_dScroll, wxID_ANY, wxT("Michael Rooke"), wxDefaultPosition, wxDefaultSize, 0 ); m_dMichaelRooke->Wrap( -1 ); m_dSizer->Add( m_dMichaelRooke, 0, wxALL, 5 ); m_dTNCOM = new wxStaticText( m_dScroll, wxID_ANY, wxT("TNCOM"), wxDefaultPosition, wxDefaultSize, 0 ); m_dTNCOM->Wrap( -1 ); m_dSizer->Add( m_dTNCOM, 0, wxALL, 5 ); m_dErikWied = new wxStaticText( m_dScroll, wxID_ANY, wxT("Erik Mikkel Wied"), wxDefaultPosition, wxDefaultSize, 0 ); m_dErikWied->Wrap( -1 ); m_dSizer->Add( m_dErikWied, 0, wxALL, 5 ); m_dRobertDuering = new wxStaticText( m_dScroll, wxID_ANY, wxT("Robert Duering"), wxDefaultPosition, wxDefaultSize, 0 ); m_dRobertDuering->Wrap( -1 ); m_dSizer->Add( m_dRobertDuering, 0, wxALL, 5 ); m_dJimDeitch = new wxStaticText( m_dScroll, wxID_ANY, wxT("Jim Deitch"), wxDefaultPosition, wxDefaultSize, 0 ); m_dJimDeitch->Wrap( -1 ); m_dSizer->Add( m_dJimDeitch, 0, wxALL, 5 ); m_dNooElec = new wxStaticText( m_dScroll, wxID_ANY, wxT("NooElec Inc. / nooelec.com"), wxDefaultPosition, wxDefaultSize, 0 ); m_dNooElec->Wrap( -1 ); m_dSizer->Add( m_dNooElec, 0, wxALL, 5 ); m_dDavidAhlgren = new wxStaticText( m_dScroll, wxID_ANY, wxT("David Ahlgren"), wxDefaultPosition, wxDefaultSize, 0 ); m_dDavidAhlgren->Wrap( -1 ); m_dSizer->Add( m_dDavidAhlgren, 0, wxALL, 5 ); m_dRonaldCook = new wxStaticText( m_dScroll, wxID_ANY, wxT("Ronald Cook"), wxDefaultPosition, wxDefaultSize, 0 ); m_dRonaldCook->Wrap( -1 ); m_dSizer->Add( m_dRonaldCook, 0, wxALL, 5 ); m_dEricPeterson = new wxStaticText( m_dScroll, wxID_ANY, wxT("Eric Peterson"), wxDefaultPosition, wxDefaultSize, 0 ); m_dEricPeterson->Wrap( -1 ); m_dSizer->Add( m_dEricPeterson, 0, wxALL, 5 ); m_dGeoDistributing = new wxStaticText( m_dScroll, wxID_ANY, wxT("Geo Distributing"), wxDefaultPosition, wxDefaultSize, 0 ); m_dGeoDistributing->Wrap( -1 ); m_dSizer->Add( m_dGeoDistributing, 0, wxALL, 5 ); m_dJamesCarson = new wxStaticText( m_dScroll, wxID_ANY, wxT("James Carson"), wxDefaultPosition, wxDefaultSize, 0 ); m_dJamesCarson->Wrap( -1 ); m_dSizer->Add( m_dJamesCarson, 0, wxALL, 5 ); m_dCraigWilliams = new wxStaticText( m_dScroll, wxID_ANY, wxT("Craig Williams"), wxDefaultPosition, wxDefaultSize, 0 ); m_dCraigWilliams->Wrap( -1 ); m_dSizer->Add( m_dCraigWilliams, 0, wxALL, 5 ); m_dRudolfShaffer = new wxStaticText( m_dScroll, wxID_ANY, wxT("Rudolf Schaffer"), wxDefaultPosition, wxDefaultSize, 0 ); m_dRudolfShaffer->Wrap( -1 ); m_dSizer->Add( m_dRudolfShaffer, 0, wxALL, 5 ); m_dJohnKaton = new wxStaticText( m_dScroll, wxID_ANY, wxT("John Katon"), wxDefaultPosition, wxDefaultSize, 0 ); m_dJohnKaton->Wrap( -1 ); m_dSizer->Add( m_dJohnKaton, 0, wxALL, 5 ); m_dVincentSonnier = new wxStaticText( m_dScroll, wxID_ANY, wxT("Vincent Sonnier"), wxDefaultPosition, wxDefaultSize, 0 ); m_dVincentSonnier->Wrap( -1 ); m_dSizer->Add( m_dVincentSonnier, 0, wxALL, 5 ); m_dCorq = new wxStaticText( m_dScroll, wxID_ANY, wxT("corq's auctions/L. Easterly LTD (x 4)"), wxDefaultPosition, wxDefaultSize, 0 ); m_dCorq->Wrap( -1 ); m_dSizer->Add( m_dCorq, 0, wxALL, 5 ); m_dIvanAlekseev = new wxStaticText( m_dScroll, wxID_ANY, wxT("Ivan Alekseev"), wxDefaultPosition, wxDefaultSize, 0 ); m_dIvanAlekseev->Wrap( -1 ); m_dSizer->Add( m_dIvanAlekseev, 0, wxALL, 5 ); m_dOleJorgenKolsrud = new wxStaticText( m_dScroll, wxID_ANY, wxT("Ole-Jørgen Næss Kolsrud"), wxDefaultPosition, wxDefaultSize, 0 ); m_dOleJorgenKolsrud->Wrap( -1 ); m_dSizer->Add( m_dOleJorgenKolsrud, 0, wxALL, 5 ); m_dHenrikJagemyr = new wxStaticText( m_dScroll, wxID_ANY, wxT("Henrik Jagemyr"), wxDefaultPosition, wxDefaultSize, 0 ); m_dHenrikJagemyr->Wrap( -1 ); m_dSizer->Add( m_dHenrikJagemyr, 0, wxALL, 5 ); m_dPeterHaines = new wxStaticText( m_dScroll, wxID_ANY, wxT("Peter Haines"), wxDefaultPosition, wxDefaultSize, 0 ); m_dPeterHaines->Wrap( -1 ); m_dSizer->Add( m_dPeterHaines, 0, wxALL, 5 ); m_dLeonAbrassart = new wxStaticText( m_dScroll, wxID_ANY, wxT("Leon Abrassart"), wxDefaultPosition, wxDefaultSize, 0 ); m_dLeonAbrassart->Wrap( -1 ); m_dSizer->Add( m_dLeonAbrassart, 0, wxALL, 5 ); m_dGeorgeTalbot = new wxStaticText( m_dScroll, wxID_ANY, wxT("George Alan Talbot"), wxDefaultPosition, wxDefaultSize, 0 ); m_dGeorgeTalbot->Wrap( -1 ); m_dSizer->Add( m_dGeorgeTalbot, 0, wxALL, 5 ); m_dFranciscoPuerta = new wxStaticText( m_dScroll, wxID_ANY, wxT("Francisco Borja Marcos de la Puerta"), wxDefaultPosition, wxDefaultSize, 0 ); m_dFranciscoPuerta->Wrap( -1 ); m_dSizer->Add( m_dFranciscoPuerta, 0, wxALL, 5 ); m_dRonaldLundeen = new wxStaticText( m_dScroll, wxID_ANY, wxT("Ronald A. Lundeen"), wxDefaultPosition, wxDefaultSize, 0 ); m_dRonaldLundeen->Wrap( -1 ); m_dSizer->Add( m_dRonaldLundeen, 0, wxALL, 5 ); m_dWalterHorbert = new wxStaticText( m_dScroll, wxID_ANY, wxT("Walter Horbert"), wxDefaultPosition, wxDefaultSize, 0 ); m_dWalterHorbert->Wrap( -1 ); m_dSizer->Add( m_dWalterHorbert, 0, wxALL, 5 ); m_dWilliamLD = new wxStaticText( m_dScroll, wxID_ANY, wxT("William Lloyd-Davies"), wxDefaultPosition, wxDefaultSize, 0 ); m_dWilliamLD->Wrap( -1 ); m_dSizer->Add( m_dWilliamLD, 0, wxALL, 5 ); m_dBratislavArandjelovic = new wxStaticText( m_dScroll, wxID_ANY, wxT("Bratislav Arandjelovic"), wxDefaultPosition, wxDefaultSize, 0 ); m_dBratislavArandjelovic->Wrap( -1 ); m_dSizer->Add( m_dBratislavArandjelovic, 0, wxALL, 5 ); m_dGaryMartin = new wxStaticText( m_dScroll, wxID_ANY, wxT("Gary Martin"), wxDefaultPosition, wxDefaultSize, 0 ); m_dGaryMartin->Wrap( -1 ); m_dSizer->Add( m_dGaryMartin, 0, wxALL, 5 ); m_dEinarsRepse = new wxStaticText( m_dScroll, wxID_ANY, wxT("Einars Repse"), wxDefaultPosition, wxDefaultSize, 0 ); m_dEinarsRepse->Wrap( -1 ); m_dSizer->Add( m_dEinarsRepse, 0, wxALL, 5 ); m_dTimothyGatton = new wxStaticText( m_dScroll, wxID_ANY, wxT("Timothy Gatton"), wxDefaultPosition, wxDefaultSize, 0 ); m_dTimothyGatton->Wrap( -1 ); m_dSizer->Add( m_dTimothyGatton, 0, wxALL, 5 ); m_dStephenCuccio = new wxStaticText( m_dScroll, wxID_ANY, wxT("Stephen Cuccio"), wxDefaultPosition, wxDefaultSize, 0 ); m_dStephenCuccio->Wrap( -1 ); m_dSizer->Add( m_dStephenCuccio, 0, wxALL, 5 ); m_dKeshavlalPatel = new wxStaticText( m_dScroll, wxID_ANY, wxT("Keshavlal Patel"), wxDefaultPosition, wxDefaultSize, 0 ); m_dKeshavlalPatel->Wrap( -1 ); m_dSizer->Add( m_dKeshavlalPatel, 0, wxALL, 5 ); m_dBobSchatzman = new wxStaticText( m_dScroll, wxID_ANY, wxT("Bob Schatzman"), wxDefaultPosition, wxDefaultSize, 0 ); m_dBobSchatzman->Wrap( -1 ); m_dSizer->Add( m_dBobSchatzman, 0, wxALL, 5 ); m_dRobertRoss = new wxStaticText( m_dScroll, wxID_ANY, wxT("Robert Ross"), wxDefaultPosition, wxDefaultSize, 0 ); m_dRobertRoss->Wrap( -1 ); m_dSizer->Add( m_dRobertRoss, 0, wxALL, 5 ); m_dRobertoBellotti = new wxStaticText( m_dScroll, wxID_ANY, wxT("Roberto Bellotti"), wxDefaultPosition, wxDefaultSize, 0 ); m_dRobertoBellotti->Wrap( -1 ); m_dSizer->Add( m_dRobertoBellotti, 0, wxALL, 5 ); m_dSergeVanderTorre = new wxStaticText( m_dScroll, wxID_ANY, wxT("Serge Van der Torre"), wxDefaultPosition, wxDefaultSize, 0 ); m_dSergeVanderTorre->Wrap( -1 ); m_dSizer->Add( m_dSergeVanderTorre, 0, wxALL, 5 ); m_dDieterSchneider = new wxStaticText( m_dScroll, wxID_ANY, wxT("Dieter Schneider"), wxDefaultPosition, wxDefaultSize, 0 ); m_dDieterSchneider->Wrap( -1 ); m_dSizer->Add( m_dDieterSchneider, 0, wxALL, 5 ); m_dPetrikaJaneku = new wxStaticText( m_dScroll, wxID_ANY, wxT("Petrika Janeku"), wxDefaultPosition, wxDefaultSize, 0 ); m_dPetrikaJaneku->Wrap( -1 ); m_dSizer->Add( m_dPetrikaJaneku, 0, wxALL, 5 ); m_dChadMyslinsky = new wxStaticText( m_dScroll, wxID_ANY, wxT("Chad Myslinsky"), wxDefaultPosition, wxDefaultSize, 0 ); m_dChadMyslinsky->Wrap( -1 ); m_dSizer->Add( m_dChadMyslinsky, 0, wxALL, 5 ); m_dCharlieBruckner = new wxStaticText( m_dScroll, wxID_ANY, wxT("Charlie Bruckner"), wxDefaultPosition, wxDefaultSize, 0 ); m_dCharlieBruckner->Wrap( -1 ); m_dSizer->Add( m_dCharlieBruckner, 0, wxALL, 5 ); m_dJordanParker = new wxStaticText( m_dScroll, wxID_ANY, wxT("Jordan Parker"), wxDefaultPosition, wxDefaultSize, 0 ); m_dJordanParker->Wrap( -1 ); m_dSizer->Add( m_dJordanParker, 0, wxALL, 5 ); m_dRobertChave = new wxStaticText( m_dScroll, wxID_ANY, wxT("Robert Chave"), wxDefaultPosition, wxDefaultSize, 0 ); m_dRobertChave->Wrap( -1 ); m_dSizer->Add( m_dRobertChave, 0, wxALL, 5 ); m_dMarvinCalvert = new wxStaticText( m_dScroll, wxID_ANY, wxT("Marvin Calvert"), wxDefaultPosition, wxDefaultSize, 0 ); m_dMarvinCalvert->Wrap( -1 ); m_dSizer->Add( m_dMarvinCalvert, 0, wxALL, 5 ); m_dBSizer->Add( m_dSizer, 1, wxALL|wxEXPAND, 5 ); m_dScroll->SetSizer( m_dBSizer ); m_dScroll->Layout(); m_dBSizer->Fit( m_dScroll ); m_aboutNotebook->AddPage( m_dScroll, wxT("Donations"), false ); m_stScroll = new wxScrolledWindow( m_aboutNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); m_stScroll->SetScrollRate( 5, 5 ); wxBoxSizer* m_stBSizer; m_stBSizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* m_stSizer; m_stSizer = new wxBoxSizer( wxVERTICAL ); m_stHeader = new wxStaticText( m_stScroll, wxID_ANY, wxT("Special Thanks To"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE ); m_stHeader->Wrap( -1 ); m_stHeader->SetFont( wxFont( 15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); m_stSizer->Add( m_stHeader, 0, wxALL, 5 ); m_stDivider1 = new wxStaticLine( m_stScroll, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); m_stSizer->Add( m_stDivider1, 0, wxEXPAND | wxALL, 5 ); m_stSoapyDevAssistHeader = new wxStaticText( m_stScroll, wxID_ANY, wxT("SoapySDR Development and Assistance:"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE ); m_stSoapyDevAssistHeader->Wrap( -1 ); m_stSoapyDevAssistHeader->SetFont( wxFont( 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) ); m_stSizer->Add( m_stSoapyDevAssistHeader, 0, wxALL, 5 ); m_stJoshBlum = new wxStaticText( m_stScroll, wxID_ANY, wxT("Josh Blum / @guruofquality / pothosware.com"), wxDefaultPosition, wxDefaultSize, 0 ); m_stJoshBlum->Wrap( -1 ); m_stSizer->Add( m_stJoshBlum, 0, wxALL, 5 ); m_stDivider2 = new wxStaticLine( m_stScroll, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); m_stSizer->Add( m_stDivider2, 0, wxEXPAND | wxALL, 5 ); m_stLiquidDSPHeader = new wxStaticText( m_stScroll, wxID_ANY, wxT("Liquid-DSP Development and Assistance:"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE ); m_stLiquidDSPHeader->Wrap( -1 ); m_stLiquidDSPHeader->SetFont( wxFont( 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) ); m_stSizer->Add( m_stLiquidDSPHeader, 0, wxALL, 5 ); m_stJosephGaeddert = new wxStaticText( m_stScroll, wxID_ANY, wxT("Joseph D. Gaeddert / @jgaeddert / liquidsdr.com"), wxDefaultPosition, wxDefaultSize, 0 ); m_stJosephGaeddert->Wrap( -1 ); m_stSizer->Add( m_stJosephGaeddert, 0, wxALL, 5 ); m_stDivider3 = new wxStaticLine( m_stScroll, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); m_stSizer->Add( m_stDivider3, 0, wxEXPAND | wxALL, 5 ); m_stIdeasDirectionsHeader = new wxStaticText( m_stScroll, wxID_ANY, wxT("Ideas, Direction && Encouragement:"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE ); m_stIdeasDirectionsHeader->Wrap( -1 ); m_stIdeasDirectionsHeader->SetFont( wxFont( 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) ); m_stSizer->Add( m_stIdeasDirectionsHeader, 0, wxALL, 5 ); m_stTonMachielsen = new wxStaticText( m_stScroll, wxID_ANY, wxT("Ton Machielsen / @Toontje / @EA3HOE "), wxDefaultPosition, wxDefaultSize, 0 ); m_stTonMachielsen->Wrap( -1 ); m_stSizer->Add( m_stTonMachielsen, 0, wxALL, 5 ); m_stMikeLadd = new wxStaticText( m_stScroll, wxID_ANY, wxT("Mike Ladd / KD2KOG.com"), wxDefaultPosition, wxDefaultSize, 0 ); m_stMikeLadd->Wrap( -1 ); m_stSizer->Add( m_stMikeLadd, 0, wxALL, 5 ); m_stSDRplay = new wxStaticText( m_stScroll, wxID_ANY, wxT("SDRplay team / @SDRplay / SDRplay.com"), wxDefaultPosition, wxDefaultSize, 0 ); m_stSDRplay->Wrap( -1 ); m_stSizer->Add( m_stSDRplay, 0, wxALL, 5 ); m_stSDRplayFB = new wxStaticText( m_stScroll, wxID_ANY, wxT("SDRplay Facebook group"), wxDefaultPosition, wxDefaultSize, 0 ); m_stSDRplayFB->Wrap( -1 ); m_stSizer->Add( m_stSDRplayFB, 0, wxALL, 5 ); m_stPaulWarren = new wxStaticText( m_stScroll, wxID_ANY, wxT("Paul Warren / @pwarren"), wxDefaultPosition, wxDefaultSize, 0 ); m_stPaulWarren->Wrap( -1 ); m_stSizer->Add( m_stPaulWarren, 0, wxALL, 5 ); m_stSegesdiKaroly = new wxStaticText( m_stScroll, wxID_ANY, wxT("Segesdi Károly / @jazzkutya"), wxDefaultPosition, wxDefaultSize, 0 ); m_stSegesdiKaroly->Wrap( -1 ); m_stSizer->Add( m_stSegesdiKaroly, 0, wxALL, 5 ); m_stRedditRTLSDR = new wxStaticText( m_stScroll, wxID_ANY, wxT("Reddit RTL-SDR group /r/rtlsdr"), wxDefaultPosition, wxDefaultSize, 0 ); m_stRedditRTLSDR->Wrap( -1 ); m_stSizer->Add( m_stRedditRTLSDR, 0, wxALL, 5 ); m_stNooElec = new wxStaticText( m_stScroll, wxID_ANY, wxT("NooElec team / NooElec.com"), wxDefaultPosition, wxDefaultSize, 0 ); m_stNooElec->Wrap( -1 ); m_stSizer->Add( m_stNooElec, 0, wxALL, 5 ); m_stGHIssues = new wxStaticText( m_stScroll, wxID_ANY, wxT("Everyone who's contributed to the GitHub issues; thanks!"), wxDefaultPosition, wxDefaultSize, 0 ); m_stGHIssues->Wrap( -1 ); m_stSizer->Add( m_stGHIssues, 0, wxALL, 5 ); m_stNominate = new wxStaticText( m_stScroll, wxID_ANY, wxT("Please feel free to nominate anyone we might have missed."), wxDefaultPosition, wxDefaultSize, 0 ); m_stNominate->Wrap( -1 ); m_stSizer->Add( m_stNominate, 0, wxALL, 5 ); m_stBSizer->Add( m_stSizer, 1, wxALL|wxEXPAND, 5 ); m_stScroll->SetSizer( m_stBSizer ); m_stScroll->Layout(); m_stBSizer->Fit( m_stScroll ); m_aboutNotebook->AddPage( m_stScroll, wxT("Special Thanks"), false ); dlgSizer->Add( m_aboutNotebook, 1, wxEXPAND | wxALL, 5 ); this->SetSizer( dlgSizer ); this->Layout(); this->Centre( wxBOTH ); } AboutDialogBase::~AboutDialogBase() { } CubicSDR-0.2.3/src/forms/Dialog/AboutDialogBase.h000066400000000000000000000110031322677621400213710ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Nov 6 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #ifndef __ABOUTDIALOGBASE_H__ #define __ABOUTDIALOGBASE_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Class AboutDialogBase /////////////////////////////////////////////////////////////////////////////// class AboutDialogBase : public wxDialog { private: protected: wxPanel* m_hPanel; wxStaticText* m_appName; wxNotebook* m_aboutNotebook; wxScrolledWindow* m_dbScroll; wxStaticText* m_dbHeader; wxStaticText* m_dbGHHeader; wxStaticText* m_dbTwitter; wxStaticText* m_dbCharlesCliffe; wxStaticText* m_dbghCC; wxStaticText* m_dbtCC; wxStaticText* m_dbVincentSonnier; wxStaticText* m_dbghVS; wxStaticText* m_dbtVS; wxStaticLine* m_dbDivider1; wxStaticText* m_cContributorsHeader; wxStaticText* m_cGitHub; wxStaticText* m_cCorneLukken; wxStaticText* m_cghCL; wxStaticText* m_cStainislawPitucha; wxStaticText* m_cghSP; wxStaticText* m_cghStefanTalpalaru; wxStaticText* m_cghST; wxStaticText* m_cCrisMotch; wxStaticText* m_cghCM; wxStaticText* m_cMariuszRyndzionek; wxStaticText* m_cghMR; wxStaticText* m_cJiangWei; wxStaticText* m_cghJW; wxStaticText* m_cTomSwartz; wxStaticText* m_cghTS; wxStaticText* m_cInfinityCyberworks; wxStaticText* m_cghIC; wxScrolledWindow* m_dScroll; wxStaticText* m_dHeader; wxStaticLine* m_dDivider1; wxStaticText* m_dSDRplay; wxStaticText* m_dMichaelLadd; wxStaticText* m_dAutoMotiveTemplates; wxStaticText* m_dJorgeMorales; wxStaticText* m_dMichaelRooke; wxStaticText* m_dTNCOM; wxStaticText* m_dErikWied; wxStaticText* m_dRobertDuering; wxStaticText* m_dJimDeitch; wxStaticText* m_dNooElec; wxStaticText* m_dDavidAhlgren; wxStaticText* m_dRonaldCook; wxStaticText* m_dEricPeterson; wxStaticText* m_dGeoDistributing; wxStaticText* m_dJamesCarson; wxStaticText* m_dCraigWilliams; wxStaticText* m_dRudolfShaffer; wxStaticText* m_dJohnKaton; wxStaticText* m_dVincentSonnier; wxStaticText* m_dCorq; wxStaticText* m_dIvanAlekseev; wxStaticText* m_dOleJorgenKolsrud; wxStaticText* m_dHenrikJagemyr; wxStaticText* m_dPeterHaines; wxStaticText* m_dLeonAbrassart; wxStaticText* m_dGeorgeTalbot; wxStaticText* m_dFranciscoPuerta; wxStaticText* m_dRonaldLundeen; wxStaticText* m_dWalterHorbert; wxStaticText* m_dWilliamLD; wxStaticText* m_dBratislavArandjelovic; wxStaticText* m_dGaryMartin; wxStaticText* m_dEinarsRepse; wxStaticText* m_dTimothyGatton; wxStaticText* m_dStephenCuccio; wxStaticText* m_dKeshavlalPatel; wxStaticText* m_dBobSchatzman; wxStaticText* m_dRobertRoss; wxStaticText* m_dRobertoBellotti; wxStaticText* m_dSergeVanderTorre; wxStaticText* m_dDieterSchneider; wxStaticText* m_dPetrikaJaneku; wxStaticText* m_dChadMyslinsky; wxStaticText* m_dCharlieBruckner; wxStaticText* m_dJordanParker; wxStaticText* m_dRobertChave; wxStaticText* m_dMarvinCalvert; wxScrolledWindow* m_stScroll; wxStaticText* m_stHeader; wxStaticLine* m_stDivider1; wxStaticText* m_stSoapyDevAssistHeader; wxStaticText* m_stJoshBlum; wxStaticLine* m_stDivider2; wxStaticText* m_stLiquidDSPHeader; wxStaticText* m_stJosephGaeddert; wxStaticLine* m_stDivider3; wxStaticText* m_stIdeasDirectionsHeader; wxStaticText* m_stTonMachielsen; wxStaticText* m_stMikeLadd; wxStaticText* m_stSDRplay; wxStaticText* m_stSDRplayFB; wxStaticText* m_stPaulWarren; wxStaticText* m_stSegesdiKaroly; wxStaticText* m_stRedditRTLSDR; wxStaticText* m_stNooElec; wxStaticText* m_stGHIssues; wxStaticText* m_stNominate; public: AboutDialogBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("About"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 530,420 ), long style = wxDEFAULT_DIALOG_STYLE ); ~AboutDialogBase(); }; #endif //__ABOUTDIALOGBASE_H__ CubicSDR-0.2.3/src/forms/Dialog/ActionDialog.cpp000066400000000000000000000023461322677621400213060ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ActionDialog.h" ActionDialog *ActionDialog::activeDialog = nullptr; ActionDialog::ActionDialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : ActionDialogBase(parent, id, title, pos, size, style) { } ActionDialog::~ActionDialog() { } void ActionDialog::showDialog(ActionDialog *dlg) { if (activeDialog) { // rejected delete dlg; return; } activeDialog = dlg; dlg->Layout(); dlg->Fit(); dlg->ShowModal(); } ActionDialog *ActionDialog::getActiveDialog() { return activeDialog; } void ActionDialog::setActiveDialog(ActionDialog *dlg) { activeDialog = dlg; } void ActionDialog::onClickCancel( wxCommandEvent& event ) { ActionDialog *dlg = activeDialog; ActionDialog::setActiveDialog(nullptr); dlg->EndModal(0); doClickCancel(); delete dlg; } void ActionDialog::onClickOK( wxCommandEvent& event ) { ActionDialog *dlg = activeDialog; ActionDialog::setActiveDialog(nullptr); dlg->EndModal(0); doClickOK(); delete dlg; } void ActionDialog::doClickCancel() { } void ActionDialog::doClickOK() { } CubicSDR-0.2.3/src/forms/Dialog/ActionDialog.h000066400000000000000000000014141322677621400207460ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ActionDialogBase.h" #include class ActionDialog : public ActionDialogBase { public: ActionDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("QuestionTitle"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); ~ActionDialog(); void onClickCancel( wxCommandEvent& event ); void onClickOK( wxCommandEvent& event ); virtual void doClickCancel(); virtual void doClickOK(); static ActionDialog *getActiveDialog(); static void setActiveDialog(ActionDialog *dlg); static void showDialog(ActionDialog *dlg); private: static ActionDialog *activeDialog; }; CubicSDR-0.2.3/src/forms/Dialog/ActionDialogBase.cpp000066400000000000000000000037251322677621400221030ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "ActionDialogBase.h" /////////////////////////////////////////////////////////////////////////// ActionDialogBase::ActionDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) { this->SetSizeHints(wxDefaultSize, wxDefaultSize); wxBoxSizer* mainSizer; mainSizer = new wxBoxSizer(wxVERTICAL); m_questionText = new wxStaticText(this, wxID_ANY, wxT("Question"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); m_questionText->Wrap(-1); mainSizer->Add(m_questionText, 1, wxALL | wxEXPAND, 5); wxBoxSizer* buttonSizer; buttonSizer = new wxBoxSizer(wxHORIZONTAL); m_cancelButton = new wxButton(this, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0); buttonSizer->Add(m_cancelButton, 1, wxALL | wxEXPAND, 5); m_okButton = new wxButton(this, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0); buttonSizer->Add(m_okButton, 1, wxALL | wxEXPAND, 5); mainSizer->Add(buttonSizer, 1, wxEXPAND, 5); this->SetSizer(mainSizer); this->Layout(); mainSizer->Fit(this); this->Centre(wxBOTH); // Connect Events m_cancelButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ActionDialogBase::onClickCancel), NULL, this); m_okButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ActionDialogBase::onClickOK), NULL, this); } ActionDialogBase::~ActionDialogBase() { // Disconnect Events m_cancelButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ActionDialogBase::onClickCancel), NULL, this); m_okButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ActionDialogBase::onClickOK), NULL, this); } CubicSDR-0.2.3/src/forms/Dialog/ActionDialogBase.fbp000066400000000000000000000550721322677621400220720ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect ActionDialogBase 1000 none 0 ActionDialogBase . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT wxBOTH 1 1 impl_virtual 0 wxID_ANY ActionDialogBase wxDEFAULT_DIALOG_STYLE QuestionTitle mainSizer wxVERTICAL none 5 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Question 0 0 1 m_questionText 1 protected 1 Resizable 1 wxALIGN_CENTRE 0 -1 5 wxEXPAND 1 buttonSizer wxHORIZONTAL none 5 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 m_cancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator onClickCancel 5 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 m_okButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator onClickOK CubicSDR-0.2.3/src/forms/Dialog/ActionDialogBase.h000066400000000000000000000027461322677621400215520ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #ifndef __ACTIONDIALOGBASE_H__ #define __ACTIONDIALOGBASE_H__ #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Class ActionDialogBase /////////////////////////////////////////////////////////////////////////////// class ActionDialogBase : public wxDialog { private: protected: wxStaticText* m_questionText; wxButton* m_cancelButton; wxButton* m_okButton; // Virtual event handlers, overide them in your derived class virtual void onClickCancel(wxCommandEvent& event) { event.Skip(); } virtual void onClickOK(wxCommandEvent& event) { event.Skip(); } public: ActionDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("QuestionTitle"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE); ~ActionDialogBase(); }; #endif //__ACTIONDIALOGBASE_H__ CubicSDR-0.2.3/src/forms/Dialog/PortSelectorDialog.cpp000066400000000000000000000020011322677621400225020ustar00rootroot00000000000000#include "PortSelectorDialog.h" #include "rs232.h" #include "CubicSDR.h" PortSelectorDialog::PortSelectorDialog( wxWindow* parent, wxWindowID id, std::string defaultPort ) : PortSelectorDialogBase(parent, id) { comEnumerate(); int nPorts = comGetNoPorts(); for (int i = 0; i < nPorts; i++) { #ifdef WIN32 m_portList->Append(comGetPortName(i)); #else m_portList->Append(comGetInternalName(i)); #endif } comTerminate(); m_portSelection->SetValue(defaultPort); } void PortSelectorDialog::onListSelect( wxCommandEvent& event ) { int pSelect = m_portList->GetSelection(); if (pSelect != -1) { m_portSelection->SetValue(m_portList->GetString(pSelect)); } } void PortSelectorDialog::onCancelButton( wxCommandEvent& event ) { wxGetApp().getAppFrame()->dismissRigControlPortDialog(); } void PortSelectorDialog::onOKButton( wxCommandEvent& event ) { wxGetApp().getAppFrame()->setRigControlPort(m_portSelection->GetValue().ToStdString()); } CubicSDR-0.2.3/src/forms/Dialog/PortSelectorDialog.fbp000066400000000000000000001346231322677621400225070ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect PortSelectorDialogBase 1000 none 0 PortSelectorDialog . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT wxBOTH 1 1 impl_virtual 0 wxID_ANY PortSelectorDialogBase 304,197 wxDEFAULT_DIALOG_STYLE Select Port dlgSizer wxVERTICAL none 5 wxEXPAND|wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Select a detected port or enter your own 0 0 1 m_staticText1 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_portList 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator onListSelect 5 wxEXPAND 1 -1,30 bSizer3 wxHORIZONTAL none 5 wxALL|wxALIGN_CENTER_VERTICAL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Port 0 0 1 m_staticText2 1 protected 1 Resizable 1 0 -1 5 wxEXPAND|wxRIGHT 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_portSelection 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator 5 wxEXPAND | wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_buttonPanel 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer2 wxHORIZONTAL none 5 wxALL|wxALIGN_BOTTOM 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 m_cancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator onCancelButton 5 wxEXPAND 1 0 protected 0 5 wxALL|wxALIGN_BOTTOM 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY OK 0 0 1 m_okButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator onOKButton CubicSDR-0.2.3/src/forms/Dialog/PortSelectorDialog.h000066400000000000000000000005531322677621400221610ustar00rootroot00000000000000#include "PortSelectorDialogBase.h" class PortSelectorDialog : public PortSelectorDialogBase { public: PortSelectorDialog( wxWindow* parent, wxWindowID id = wxID_ANY, std::string defaultPort = "" ); protected: void onListSelect( wxCommandEvent& event ); void onCancelButton( wxCommandEvent& event ); void onOKButton( wxCommandEvent& event ); }; CubicSDR-0.2.3/src/forms/Dialog/PortSelectorDialogBase.cpp000066400000000000000000000062071322677621400233110ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "PortSelectorDialogBase.h" /////////////////////////////////////////////////////////////////////////// PortSelectorDialogBase::PortSelectorDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) { this->SetSizeHints(wxDefaultSize, wxDefaultSize); wxBoxSizer* dlgSizer; dlgSizer = new wxBoxSizer(wxVERTICAL); m_staticText1 = new wxStaticText(this, wxID_ANY, wxT("Select a detected port or enter your own"), wxDefaultPosition, wxDefaultSize, 0); m_staticText1->Wrap(-1); dlgSizer->Add(m_staticText1, 0, wxEXPAND | wxALL, 5); m_portList = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, 0); dlgSizer->Add(m_portList, 1, wxALL | wxEXPAND, 5); wxBoxSizer* bSizer3; bSizer3 = new wxBoxSizer(wxHORIZONTAL); bSizer3->SetMinSize(wxSize(-1, 30)); m_staticText2 = new wxStaticText(this, wxID_ANY, wxT("Port"), wxDefaultPosition, wxDefaultSize, 0); m_staticText2->Wrap(-1); bSizer3->Add(m_staticText2, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); m_portSelection = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); bSizer3->Add(m_portSelection, 1, wxEXPAND | wxRIGHT, 5); dlgSizer->Add(bSizer3, 1, wxEXPAND, 5); m_buttonPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); wxBoxSizer* bSizer2; bSizer2 = new wxBoxSizer(wxHORIZONTAL); m_cancelButton = new wxButton(m_buttonPanel, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0); bSizer2->Add(m_cancelButton, 0, wxALL | wxALIGN_BOTTOM, 5); bSizer2->Add(0, 0, 1, wxEXPAND, 5); m_okButton = new wxButton(m_buttonPanel, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0); bSizer2->Add(m_okButton, 0, wxALL | wxALIGN_BOTTOM, 5); m_buttonPanel->SetSizer(bSizer2); m_buttonPanel->Layout(); bSizer2->Fit(m_buttonPanel); dlgSizer->Add(m_buttonPanel, 0, wxEXPAND | wxALL, 5); this->SetSizer(dlgSizer); this->Layout(); this->Centre(wxBOTH); // Connect Events m_portList->Connect(wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler(PortSelectorDialogBase::onListSelect), NULL, this); m_cancelButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PortSelectorDialogBase::onCancelButton), NULL, this); m_okButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PortSelectorDialogBase::onOKButton), NULL, this); } PortSelectorDialogBase::~PortSelectorDialogBase() { // Disconnect Events m_portList->Disconnect(wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler(PortSelectorDialogBase::onListSelect), NULL, this); m_cancelButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PortSelectorDialogBase::onCancelButton), NULL, this); m_okButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PortSelectorDialogBase::onOKButton), NULL, this); } CubicSDR-0.2.3/src/forms/Dialog/PortSelectorDialogBase.h000066400000000000000000000034121322677621400227510ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #ifndef __PORTSELECTORDIALOGBASE_H__ #define __PORTSELECTORDIALOGBASE_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Class PortSelectorDialogBase /////////////////////////////////////////////////////////////////////////////// class PortSelectorDialogBase : public wxDialog { private: protected: wxStaticText* m_staticText1; wxListBox* m_portList; wxStaticText* m_staticText2; wxTextCtrl* m_portSelection; wxPanel* m_buttonPanel; wxButton* m_cancelButton; wxButton* m_okButton; // Virtual event handlers, overide them in your derived class virtual void onListSelect(wxCommandEvent& event) { event.Skip(); } virtual void onCancelButton(wxCommandEvent& event) { event.Skip(); } virtual void onOKButton(wxCommandEvent& event) { event.Skip(); } public: PortSelectorDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Select Port"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(304, 197), long style = wxDEFAULT_DIALOG_STYLE); ~PortSelectorDialogBase(); }; #endif //__PORTSELECTORDIALOGBASE_H__ CubicSDR-0.2.3/src/forms/DigitalConsole/000077500000000000000000000000001322677621400177415ustar00rootroot00000000000000CubicSDR-0.2.3/src/forms/DigitalConsole/DigitalConsole.cpp000066400000000000000000000056621322677621400233560ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "DigitalConsole.h" #include "CubicSDR.h" #include DigitalConsole::DigitalConsole( wxWindow* parent, ModemDigitalOutputConsole *doParent ): DigitalConsoleFrame( parent ), doParent(doParent) { streamWritten.store(false); streamPaused.store(false); } DigitalConsole::~DigitalConsole() { doParent->setDialog(nullptr); } void DigitalConsole::OnClose( wxCloseEvent& event ) { doParent->setDialog(nullptr); } void DigitalConsole::OnCopy( wxCommandEvent& event ) { m_dataView->SelectAll(); m_dataView->Copy(); } void DigitalConsole::OnPause( wxCommandEvent& event ) { if (streamPaused.load()) { m_pauseButton->SetLabel("Stop"); streamPaused.store(false); } else { m_pauseButton->SetLabel("Run"); streamPaused.store(true); } } void DoRefresh( wxTimerEvent& event ) { event.Skip(); } void DigitalConsole::DoRefresh( wxTimerEvent& event ) { if (streamWritten.load()) { stream_busy.lock(); m_dataView->AppendText(streamBuf.str()); streamBuf.str(""); streamWritten.store(false); stream_busy.unlock(); } } void DigitalConsole::OnClear( wxCommandEvent& event ) { m_dataView->Clear(); } void DigitalConsole::write(std::string outp) { if (streamPaused.load()) { return; } stream_busy.lock(); streamBuf << outp; streamWritten.store(true); stream_busy.unlock(); } void DigitalConsole::write(char outc) { if (streamPaused.load()) { return; } stream_busy.lock(); streamBuf << outc; streamWritten.store(true); stream_busy.unlock(); } ModemDigitalOutputConsole::ModemDigitalOutputConsole(): ModemDigitalOutput(), dialog(nullptr) { streamWritten.store(false); } ModemDigitalOutputConsole::~ModemDigitalOutputConsole() { } void ModemDigitalOutputConsole::setDialog(DigitalConsole *dialog_in) { dialog = dialog_in; if (dialog && dialogTitle != "") { dialog->SetTitle(dialogTitle); } } DigitalConsole *ModemDigitalOutputConsole::getDialog() { return dialog; } void ModemDigitalOutputConsole::Show() { if (!dialog) { return; } if (!dialog->IsShown()) { dialog->Show(); } } void ModemDigitalOutputConsole::Hide() { if (!dialog) { return; } if (dialog->IsShown()) { dialog->Hide(); } } void ModemDigitalOutputConsole::Close() { if (!dialog) { return; } dialog->Hide(); dialog->Close(); dialog = nullptr; } void ModemDigitalOutputConsole::setTitle(std::string title) { if (dialog) { dialog->SetTitle(title); } dialogTitle = title; } void ModemDigitalOutputConsole::write(std::string outp) { if (!dialog) { return; } dialog->write(outp); } void ModemDigitalOutputConsole::write(char outc) { if (!dialog) { return; } dialog->write(outc); } CubicSDR-0.2.3/src/forms/DigitalConsole/DigitalConsole.fbp000066400000000000000000000763561322677621400233530ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect DigitalConsoleFrame 1000 none 0 DigitalConsole . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT wxBOTH 1 1 impl_virtual 0 wxID_ANY DigitalConsoleFrame 441,394 wxCAPTION|wxFRAME_FLOAT_ON_PARENT|wxMAXIMIZE|wxMAXIMIZE_BOX|wxMINIMIZE|wxMINIMIZE_BOX|wxRESIZE_BORDER ; Digital Output wxWS_EX_PROCESS_UI_UPDATES wxFULL_REPAINT_ON_RESIZE|wxTAB_TRAVERSAL 1 OnClose mainSizer wxVERTICAL none 5 wxEXPAND 1 dataViewSizer wxVERTICAL none 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 ,90,90,-1,76,0 0 0 wxID_ANY 0 0 1 m_dataView 1 protected 1 Resizable 1 wxTE_CHARWRAP|wxTE_MULTILINE|wxTE_NOHIDESEL|wxTE_READONLY|wxTE_WORDWRAP 0 wxFILTER_NONE wxDefaultValidator wxWS_EX_PROCESS_UI_UPDATES wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE|wxNO_BORDER|wxSIMPLE_BORDER|wxVSCROLL 5 wxALL|wxEXPAND 0 buttonSizer wxHORIZONTAL none 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Clear 0 0 1 m_clearButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator OnClear 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Copy 0 0 1 m_copyButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator OnCopy 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Stop 0 0 1 m_pauseButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator OnPause 1 wxID_ANY m_refreshTimer 0 250 protected DoRefresh CubicSDR-0.2.3/src/forms/DigitalConsole/DigitalConsole.h000066400000000000000000000026561322677621400230230ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include #include "DigitalConsoleFrame.h" #include "ModemDigital.h" class ModemDigitalOutputConsole; class DigitalConsole: public DigitalConsoleFrame { public: DigitalConsole( wxWindow* parent, ModemDigitalOutputConsole *doParent ); ~DigitalConsole(); void write(std::string outp); void write(char outc); private: void DoRefresh( wxTimerEvent& event ); void OnClose( wxCloseEvent& event ); void OnClear( wxCommandEvent& event ); void OnCopy( wxCommandEvent& event ); void OnPause( wxCommandEvent& event ); std::stringstream streamBuf; std::mutex stream_busy; std::atomic streamWritten; std::atomic streamPaused; ModemDigitalOutputConsole *doParent; }; class ModemDigitalOutputConsole: public ModemDigitalOutput { public: ModemDigitalOutputConsole(); ~ModemDigitalOutputConsole(); void setDialog(DigitalConsole *dialog_in); DigitalConsole *getDialog(); void setTitle(std::string title); void write(std::string outp); void write(char outc); void Show(); void Hide(); void Close(); private: DigitalConsole *dialog; std::stringstream streamBuf; std::mutex stream_busy; std::atomic streamWritten; std::string dialogTitle; }; CubicSDR-0.2.3/src/forms/DigitalConsole/DigitalConsoleFrame.cpp000066400000000000000000000063471322677621400243320ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "DigitalConsoleFrame.h" /////////////////////////////////////////////////////////////////////////// DigitalConsoleFrame::DigitalConsoleFrame(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) { this->SetSizeHints(wxDefaultSize, wxDefaultSize); this->SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES); wxBoxSizer* mainSizer; mainSizer = new wxBoxSizer(wxVERTICAL); wxBoxSizer* dataViewSizer; dataViewSizer = new wxBoxSizer(wxVERTICAL); m_dataView = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CHARWRAP | wxTE_MULTILINE | wxTE_NOHIDESEL | wxTE_READONLY | wxTE_WORDWRAP | wxALWAYS_SHOW_SB | wxFULL_REPAINT_ON_RESIZE | wxNO_BORDER | wxSIMPLE_BORDER | wxVSCROLL); m_dataView->SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES); m_dataView->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString)); dataViewSizer->Add(m_dataView, 1, wxEXPAND, 5); mainSizer->Add(dataViewSizer, 1, wxEXPAND, 5); wxBoxSizer* buttonSizer; buttonSizer = new wxBoxSizer(wxHORIZONTAL); m_clearButton = new wxButton(this, wxID_ANY, wxT("Clear"), wxDefaultPosition, wxDefaultSize, 0); buttonSizer->Add(m_clearButton, 1, wxEXPAND, 5); m_copyButton = new wxButton(this, wxID_ANY, wxT("Copy"), wxDefaultPosition, wxDefaultSize, 0); buttonSizer->Add(m_copyButton, 1, wxEXPAND, 5); m_pauseButton = new wxButton(this, wxID_ANY, wxT("Stop"), wxDefaultPosition, wxDefaultSize, 0); buttonSizer->Add(m_pauseButton, 1, wxEXPAND, 5); mainSizer->Add(buttonSizer, 0, wxALL | wxEXPAND, 5); this->SetSizer(mainSizer); this->Layout(); m_refreshTimer.SetOwner(this, wxID_ANY); m_refreshTimer.Start(250); this->Centre(wxBOTH); // Connect Events this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(DigitalConsoleFrame::OnClose)); m_clearButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnClear), NULL, this); m_copyButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnCopy), NULL, this); m_pauseButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnPause), NULL, this); this->Connect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(DigitalConsoleFrame::DoRefresh)); } DigitalConsoleFrame::~DigitalConsoleFrame() { // Disconnect Events this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(DigitalConsoleFrame::OnClose)); m_clearButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnClear), NULL, this); m_copyButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnCopy), NULL, this); m_pauseButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnPause), NULL, this); this->Disconnect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(DigitalConsoleFrame::DoRefresh)); } CubicSDR-0.2.3/src/forms/DigitalConsole/DigitalConsoleFrame.h000066400000000000000000000035741322677621400237760ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #ifndef __DIGITALCONSOLEFRAME_H__ #define __DIGITALCONSOLEFRAME_H__ #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Class DigitalConsoleFrame /////////////////////////////////////////////////////////////////////////////// class DigitalConsoleFrame : public wxFrame { private: protected: wxTextCtrl* m_dataView; wxButton* m_clearButton; wxButton* m_copyButton; wxButton* m_pauseButton; wxTimer m_refreshTimer; // Virtual event handlers, overide them in your derived class virtual void OnClose(wxCloseEvent& event) { event.Skip(); } virtual void OnClear(wxCommandEvent& event) { event.Skip(); } virtual void OnCopy(wxCommandEvent& event) { event.Skip(); } virtual void OnPause(wxCommandEvent& event) { event.Skip(); } virtual void DoRefresh(wxTimerEvent& event) { event.Skip(); } public: DigitalConsoleFrame(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Digital Output"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(441, 394), long style = wxCAPTION | wxFRAME_FLOAT_ON_PARENT | wxMAXIMIZE | wxMAXIMIZE_BOX | wxMINIMIZE | wxMINIMIZE_BOX | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxTAB_TRAVERSAL); ~DigitalConsoleFrame(); }; #endif //__DIGITALCONSOLEFRAME_H__ CubicSDR-0.2.3/src/forms/SDRDevices/000077500000000000000000000000001322677621400167745ustar00rootroot00000000000000CubicSDR-0.2.3/src/forms/SDRDevices/SDRDeviceAdd.cpp000066400000000000000000000034151322677621400216640ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SDRDeviceAdd.h" #include "SDREnumerator.h" SDRDeviceAddDialog::SDRDeviceAddDialog( wxWindow* parent ): SDRDeviceAddForm( parent ) { okPressed = false; selectedModule = ""; moduleParam = ""; selectedModule = "SoapyRemote"; m_soapyModule->Append("SoapyRemote"); m_paramLabel->SetLabel("Remote Address (address[:port])"); std::vector &factories = SDREnumerator::getFactories(); std::vector::iterator factory_i; for (factory_i = factories.begin(); factory_i != factories.end(); factory_i++) { if (*factory_i != "remote" && *factory_i != "null") { m_soapyModule->Append(*factory_i); } } } void SDRDeviceAddDialog::OnSoapyModuleChanged( wxCommandEvent& /* event */) { wxString strSel = m_soapyModule->GetStringSelection(); selectedModule = strSel.ToStdString(); if (selectedModule == "SoapyRemote") { m_paramLabel->SetLabelText("Remote Address (address[:port])"); } else { m_paramLabel->SetLabel("SoapySDR Device Parameters, i.e. 'addr=192.168.1.105'"); } } void SDRDeviceAddDialog::OnCancelButton( wxCommandEvent& /* event */) { okPressed = false; Close(true); } void SDRDeviceAddDialog::OnOkButton( wxCommandEvent& /* event */) { wxString strSel = m_soapyModule->GetStringSelection(); selectedModule = strSel.ToStdString(); moduleParam = m_paramText->GetValue().ToStdString(); okPressed = true; Close(true); } bool SDRDeviceAddDialog::wasOkPressed() { return okPressed; } std::string SDRDeviceAddDialog::getSelectedModule() { return selectedModule; } std::string SDRDeviceAddDialog::getModuleParam() { return moduleParam; } CubicSDR-0.2.3/src/forms/SDRDevices/SDRDeviceAdd.fbp000066400000000000000000001224161322677621400216540ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect SDRDeviceAddForm 1000 none 0 SDRDeviceAddForm . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT wxBOTH 1 1 impl_virtual 0 wxID_ANY SDRDeviceAddForm 395,293 wxDEFAULT_DIALOG_STYLE Add SoapySDR Device bSizer6 wxVERTICAL none 8 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY Manually add a SoapyRemote or SoapySDR device. Useful for a device that is not detected automatically. 0 0 1 m_staticText4 1 protected 1 Resizable 1 0 -1 5 wxEXPAND 1 0 protected 0 8 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_soapyModule 1 protected 1 Resizable 0 1 0 wxFILTER_NONE wxDefaultValidator OnSoapyModuleChanged 5 wxEXPAND 1 0 protected 0 8 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY <Parameter> 0 0 1 m_paramLabel 1 protected 1 Resizable 1 0 -1 8 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 -1,-1 0 -1,48 1 m_paramText 1 protected 1 Resizable 1 wxTE_DONTWRAP 0 wxFILTER_NONE wxDefaultValidator wxHSCROLL 5 wxEXPAND 1 0 protected 0 8 wxEXPAND 1 bSizer7 wxHORIZONTAL none 5 wxEXPAND 1 0 protected 0 2 wxALL 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Cancel 0 0 1 m_cancelButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator OnCancelButton 2 wxALL 0 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Ok 0 0 1 m_OkButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator OnOkButton 5 wxEXPAND 1 0 protected 0 CubicSDR-0.2.3/src/forms/SDRDevices/SDRDeviceAdd.h000066400000000000000000000010761322677621400213320ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "SDRDeviceAddForm.h" class SDRDeviceAddDialog : public SDRDeviceAddForm { public: SDRDeviceAddDialog( wxWindow* parent ); void OnSoapyModuleChanged( wxCommandEvent& event ); void OnCancelButton( wxCommandEvent& event ); void OnOkButton( wxCommandEvent& event ); bool wasOkPressed(); std::string getSelectedModule(); std::string getModuleParam(); private: bool okPressed; std::string selectedModule; std::string moduleParam; };CubicSDR-0.2.3/src/forms/SDRDevices/SDRDeviceAddForm.cpp000066400000000000000000000060101322677621400225020ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "SDRDeviceAddForm.h" /////////////////////////////////////////////////////////////////////////// SDRDeviceAddForm::SDRDeviceAddForm(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) { this->SetSizeHints(wxDefaultSize, wxDefaultSize); wxBoxSizer* bSizer6; bSizer6 = new wxBoxSizer(wxVERTICAL); m_staticText4 = new wxStaticText(this, wxID_ANY, wxT("Manually add a SoapyRemote or SoapySDR device. \n\nUseful for a device that is not detected automatically."), wxDefaultPosition, wxDefaultSize, 0); m_staticText4->Wrap(-1); bSizer6->Add(m_staticText4, 0, wxALL, 8); bSizer6->Add(0, 0, 1, wxEXPAND, 5); wxArrayString m_soapyModuleChoices; m_soapyModule = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_soapyModuleChoices, 0); m_soapyModule->SetSelection(0); bSizer6->Add(m_soapyModule, 0, wxALL, 8); bSizer6->Add(0, 0, 1, wxEXPAND, 5); m_paramLabel = new wxStaticText(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0); m_paramLabel->Wrap(-1); bSizer6->Add(m_paramLabel, 0, wxALL, 8); m_paramText = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_DONTWRAP | wxHSCROLL); m_paramText->SetMinSize(wxSize(-1, 48)); bSizer6->Add(m_paramText, 1, wxALL | wxEXPAND, 8); bSizer6->Add(0, 0, 1, wxEXPAND, 5); wxBoxSizer* bSizer7; bSizer7 = new wxBoxSizer(wxHORIZONTAL); bSizer7->Add(0, 0, 1, wxEXPAND, 5); m_cancelButton = new wxButton(this, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0); bSizer7->Add(m_cancelButton, 0, wxALL, 2); m_OkButton = new wxButton(this, wxID_ANY, wxT("Ok"), wxDefaultPosition, wxDefaultSize, 0); bSizer7->Add(m_OkButton, 0, wxALL, 2); bSizer6->Add(bSizer7, 1, wxEXPAND, 8); bSizer6->Add(0, 0, 1, wxEXPAND, 5); this->SetSizer(bSizer6); this->Layout(); this->Centre(wxBOTH); // Connect Events m_soapyModule->Connect(wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(SDRDeviceAddForm::OnSoapyModuleChanged), NULL, this); m_cancelButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SDRDeviceAddForm::OnCancelButton), NULL, this); m_OkButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SDRDeviceAddForm::OnOkButton), NULL, this); } SDRDeviceAddForm::~SDRDeviceAddForm() { // Disconnect Events m_soapyModule->Disconnect(wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(SDRDeviceAddForm::OnSoapyModuleChanged), NULL, this); m_cancelButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SDRDeviceAddForm::OnCancelButton), NULL, this); m_OkButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SDRDeviceAddForm::OnOkButton), NULL, this); } CubicSDR-0.2.3/src/forms/SDRDevices/SDRDeviceAddForm.h000066400000000000000000000032751322677621400221610ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #ifndef __SDRDEVICEADDFORM_H__ #define __SDRDEVICEADDFORM_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Class SDRDeviceAddForm /////////////////////////////////////////////////////////////////////////////// class SDRDeviceAddForm : public wxDialog { private: protected: wxStaticText* m_staticText4; wxChoice* m_soapyModule; wxStaticText* m_paramLabel; wxTextCtrl* m_paramText; wxButton* m_cancelButton; wxButton* m_OkButton; // Virtual event handlers, overide them in your derived class virtual void OnSoapyModuleChanged(wxCommandEvent& event) { event.Skip(); } virtual void OnCancelButton(wxCommandEvent& event) { event.Skip(); } virtual void OnOkButton(wxCommandEvent& event) { event.Skip(); } public: SDRDeviceAddForm(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Add SoapySDR Device"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(395, 293), long style = wxDEFAULT_DIALOG_STYLE); ~SDRDeviceAddForm(); }; #endif //__SDRDEVICEADDFORM_H__ CubicSDR-0.2.3/src/forms/SDRDevices/SDRDevices.cpp000066400000000000000000000533351322677621400214440ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SDRDevices.h" #include #include #include "CubicSDR.h" #include #ifdef __linux__ #include "CubicSDR.xpm" #endif SDRDevicesDialog::SDRDevicesDialog( wxWindow* parent ): devFrame( parent, wxID_ANY, wxT(CUBICSDR_INSTALL_NAME " :: SDR Devices")) { refresh = true; failed = false; m_refreshButton->Disable(); m_addRemoteButton->Disable(); m_useSelectedButton->Disable(); m_deviceTimer.Start(250); selId = nullptr; editId = nullptr; removeId = nullptr; devAddDialog = nullptr; dev = nullptr; #ifdef __linux__ SetIcon(wxICON(cubicsdr)); #elif _WIN32 SetIcon(wxICON(frame_icon)); #endif } void SDRDevicesDialog::OnClose( wxCloseEvent& /* event */) { wxGetApp().setDeviceSelectorClosed(); Destroy(); } void SDRDevicesDialog::OnDeleteItem( wxTreeEvent& event ) { event.Skip(); } wxPGProperty *SDRDevicesDialog::addArgInfoProperty(wxPropertyGrid *pg, SoapySDR::ArgInfo arg) { wxPGProperty *prop = NULL; int intVal; double floatVal; std::vector::iterator stringIter; switch (arg.type) { case SoapySDR::ArgInfo::INT: try { intVal = std::stoi(arg.value); } catch (std::invalid_argument e) { intVal = 0; } prop = pg->Append( new wxIntProperty(arg.name, wxPG_LABEL, intVal) ); if (arg.range.minimum() != arg.range.maximum()) { pg->SetPropertyAttribute( prop, wxPG_ATTR_MIN, arg.range.minimum()); pg->SetPropertyAttribute( prop, wxPG_ATTR_MAX, arg.range.maximum()); } break; case SoapySDR::ArgInfo::FLOAT: try { floatVal = std::stod(arg.value); } catch (std::invalid_argument e) { floatVal = 0; } prop = pg->Append( new wxFloatProperty(arg.name, wxPG_LABEL, floatVal) ); if (arg.range.minimum() != arg.range.maximum()) { pg->SetPropertyAttribute( prop, wxPG_ATTR_MIN, arg.range.minimum()); pg->SetPropertyAttribute( prop, wxPG_ATTR_MAX, arg.range.maximum()); } break; case SoapySDR::ArgInfo::BOOL: prop = pg->Append( new wxBoolProperty(arg.name, wxPG_LABEL, (arg.value=="true")) ); break; case SoapySDR::ArgInfo::STRING: if (arg.options.size()) { intVal = 0; prop = pg->Append( new wxEnumProperty(arg.name, wxPG_LABEL) ); for (stringIter = arg.options.begin(); stringIter != arg.options.end(); stringIter++) { std::string optName = (*stringIter); std::string displayName = optName; if (arg.optionNames.size()) { displayName = arg.optionNames[intVal]; } prop->AddChoice(displayName); if ((*stringIter)==arg.value) { prop->SetChoiceSelection(intVal); } intVal++; } } else { prop = pg->Append( new wxStringProperty(arg.name, wxPG_LABEL, arg.value) ); } break; } if (prop != NULL) { prop->SetHelpString(arg.key + ": " + arg.description); } return prop; } void SDRDevicesDialog::refreshDeviceProperties() { SDRDeviceInfo *selDev = getSelectedDevice(devTree->GetSelection()); if (selDev && selDev->isAvailable()) { dev = selDev; selId = devTree->GetSelection(); DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getName()); m_propertyGrid->Clear(); SoapySDR::Device *soapyDev = dev->getSoapyDevice(); SoapySDR::ArgInfoList args = soapyDev->getSettingInfo(); //A) General settings: name, offset, sample rate, agc, antennas (if > 1) m_propertyGrid->Append(new wxPropertyCategory("General Settings")); devSettings.clear(); //A-1) Name devSettings["name"] = m_propertyGrid->Append( new wxStringProperty("Name", wxPG_LABEL, devConfig->getDeviceName()) ); //A-2) Offset devSettings["offset"] = m_propertyGrid->Append( new wxIntProperty("Offset (KHz)", wxPG_LABEL, devConfig->getOffset() / 1000) ); //A-3) Antennas, is there are more than 1 RX antenna, else do not expose the setting. //get the saved setting const std::string& currentSetAntenna = devConfig->getAntennaName(); //compare to the list of existing antennas SoapySDR::ArgInfo antennasArg; std::vector antennaOpts = selDev->getAntennaNames(SOAPY_SDR_RX, 0); //only do something if there is more than 1 antenna if (antennaOpts.size() > 1) { //by default, choose the first of the list. std::string antennaToSelect = antennaOpts.front(); auto found_i = std::find(antennaOpts.begin(), antennaOpts.end(), currentSetAntenna); if (found_i != antennaOpts.end()) { antennaToSelect = currentSetAntenna; } else { //erroneous antenna name, re-write device config with the first choice of teh list. devConfig->setAntennaName(antennaToSelect); } //build device settings for (std::string antenna : antennaOpts) { antennasArg.options.push_back(antenna); antennasArg.optionNames.push_back(antenna); } antennasArg.type = SoapySDR::ArgInfo::STRING; antennasArg.units = ""; antennasArg.name = "Antenna"; antennasArg.key = "antenna"; antennasArg.value = antennaToSelect; devSettings["antenna"] = addArgInfoProperty(m_propertyGrid, antennasArg); deviceArgs["antenna"] = antennasArg; } //end if more than 1 antenna else { devConfig->setAntennaName(""); } //A-4) Sample_rate: long currentSampleRate = wxGetApp().getSampleRate(); long deviceSampleRate = devConfig->getSampleRate(); if (!deviceSampleRate) { deviceSampleRate = selDev->getSampleRateNear(SOAPY_SDR_RX, 0, currentSampleRate); } SoapySDR::ArgInfo sampleRateArg; std::vector rateOpts = selDev->getSampleRates(SOAPY_SDR_RX, 0); for (long rate : rateOpts) { sampleRateArg.options.push_back(std::to_string(rate)); sampleRateArg.optionNames.push_back(frequencyToStr(rate)); } sampleRateArg.type = SoapySDR::ArgInfo::STRING; sampleRateArg.units = "Hz"; sampleRateArg.name = "Sample Rate"; sampleRateArg.key = "sample_rate"; sampleRateArg.value = std::to_string(deviceSampleRate); devSettings["sample_rate"] = addArgInfoProperty(m_propertyGrid, sampleRateArg); deviceArgs["sample_rate"] = sampleRateArg; //B) Runtime Settings: runtimeArgs.clear(); runtimeProps.clear(); streamProps.clear(); if (args.size()) { m_propertyGrid->Append(new wxPropertyCategory("Run-time Settings")); for (SoapySDR::ArgInfoList::const_iterator args_i = args.begin(); args_i != args.end(); args_i++) { SoapySDR::ArgInfo arg = (*args_i); //We-reread the Device configuration, else we use the user settings. if (dev) { //Apply saved settings DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); arg.value = devConfig->getSetting(arg.key, soapyDev->readSetting(arg.key)); //use SoapyDevice data as fallback. } else { //re-read the SoapyDevice arg.value = soapyDev->readSetting(arg.key); } runtimeProps[arg.key] = addArgInfoProperty(m_propertyGrid, arg); runtimeArgs[arg.key] = arg; } } if (dev) { args = dev->getSoapyDevice()->getStreamArgsInfo(SOAPY_SDR_RX, 0); DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); ConfigSettings devStreamOpts = devConfig->getStreamOpts(); if (devStreamOpts.size()) { for (int j = 0, jMax = args.size(); j < jMax; j++) { if (devStreamOpts.find(args[j].key) != devStreamOpts.end()) { args[j].value = devStreamOpts[args[j].key]; } } } if (args.size()) { m_propertyGrid->Append(new wxPropertyCategory("Stream Settings")); for (SoapySDR::ArgInfo arg : args) { streamProps[arg.key] = addArgInfoProperty(m_propertyGrid, arg); } } } if (selDev->isManual()) { m_addRemoteButton->SetLabel("Remove"); removeId = selId; } else { m_addRemoteButton->SetLabel("Add"); removeId = nullptr; } } else if (selDev && !selDev->isAvailable() && selDev->isManual()) { m_propertyGrid->Clear(); devSettings.clear(); runtimeArgs.clear(); runtimeProps.clear(); streamProps.clear(); removeId = devTree->GetSelection(); dev = nullptr; selId = nullptr; editId = nullptr; m_addRemoteButton->SetLabel("Remove"); } else if (!selDev) { m_addRemoteButton->SetLabel("Add"); removeId = nullptr; } } void SDRDevicesDialog::OnSelectionChanged( wxTreeEvent& event ) { refreshDeviceProperties(); event.Skip(); } void SDRDevicesDialog::OnAddRemote( wxMouseEvent& /* event */) { if (removeId != nullptr) { SDRDeviceInfo *selDev = getSelectedDevice(removeId); if (selDev) { SDREnumerator::removeManual(selDev->getDriver(),selDev->getManualParams()); m_propertyGrid->Clear(); devSettings.clear(); runtimeArgs.clear(); runtimeProps.clear(); streamProps.clear(); dev = nullptr; selId = nullptr; editId = nullptr; devTree->Delete(removeId); removeId = nullptr; m_addRemoteButton->SetLabel("Add"); } return; } devAddDialog = new SDRDeviceAddDialog(this); devAddDialog->ShowModal(); if (devAddDialog->wasOkPressed()) { std::string module = devAddDialog->getSelectedModule(); if (module == "SoapyRemote") { if (!SDREnumerator::hasRemoteModule()) { wxMessageDialog *info; info = new wxMessageDialog(NULL, wxT("Install SoapyRemote module to add remote servers.\n\nhttps://github.com/pothosware/SoapyRemote"), wxT("SoapyRemote not found."), wxOK | wxICON_ERROR); info->ShowModal(); return; } wxString remoteAddr = devAddDialog->getModuleParam(); if (!remoteAddr.Trim().empty()) { wxGetApp().addRemote(remoteAddr.Trim().ToStdString()); } devTree->Disable(); m_addRemoteButton->Disable(); m_useSelectedButton->Disable(); refresh = true; } else { std::string mod = devAddDialog->getSelectedModule(); std::string param = devAddDialog->getModuleParam(); SDREnumerator::addManual(mod, param); doRefreshDevices(); } } } SDRDeviceInfo *SDRDevicesDialog::getSelectedDevice(wxTreeItemId selId) { devItems_i = devItems.find(selId); if (devItems_i != devItems.end()) { return devItems[selId]; } return NULL; } void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) { if (dev != NULL) { SoapySDR::ArgInfoList args = dev->getSoapyDevice()->getSettingInfo(); SoapySDR::Kwargs settingArgs; SoapySDR::Kwargs streamArgs; for (SoapySDR::ArgInfo arg : args) { wxPGProperty *prop = runtimeProps[arg.key]; if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) { settingArgs[arg.key] = arg.options[prop->GetChoiceSelection()]; } else if (arg.type == SoapySDR::ArgInfo::BOOL) { settingArgs[arg.key] = (prop->GetValueAsString()=="True")?"true":"false"; } else { settingArgs[arg.key] = prop->GetValueAsString(); } } if (dev) { args = dev->getSoapyDevice()->getStreamArgsInfo(SOAPY_SDR_RX, 0); if (args.size()) { for (SoapySDR::ArgInfoList::const_iterator args_i = args.begin(); args_i != args.end(); args_i++) { SoapySDR::ArgInfo arg = (*args_i); wxPGProperty *prop = streamProps[arg.key]; if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) { streamArgs[arg.key] = arg.options[prop->GetChoiceSelection()]; } else if (arg.type == SoapySDR::ArgInfo::BOOL) { streamArgs[arg.key] = (prop->GetValueAsString()=="True")?"true":"false"; } else { streamArgs[arg.key] = prop->GetValueAsString(); } } } } AppConfig *cfg = wxGetApp().getConfig(); DeviceConfig *devConfig = cfg->getDevice(dev->getDeviceId()); devConfig->setSettings(settingArgs); devConfig->setStreamOpts(streamArgs); wxGetApp().setDeviceArgs(settingArgs); wxGetApp().setStreamArgs(streamArgs); wxGetApp().setDevice(dev,0); Close(); } event.Skip(); } void SDRDevicesDialog::OnTreeDoubleClick( wxMouseEvent& event ) { OnUseSelected(event); } void SDRDevicesDialog::OnDeviceTimer( wxTimerEvent& event ) { if (refresh) { if (wxGetApp().areModulesMissing()) { if (!failed) { wxMessageDialog *info; info = new wxMessageDialog(NULL, wxT("\nNo SoapySDR modules were found.\n\nCubicSDR requires at least one SoapySDR device support module to be installed.\n\nPlease visit https://github.com/cjcliffe/CubicSDR/wiki and in the build instructions for your platform read the 'Support Modules' section for more information."), wxT("\x28\u256F\xB0\u25A1\xB0\uFF09\u256F\uFE35\x20\u253B\u2501\u253B"), wxOK | wxICON_ERROR); info->ShowModal(); failed = true; } return; } if (wxGetApp().areDevicesEnumerating() || !wxGetApp().areDevicesReady()) { std::string msg = wxGetApp().getNotification(); devStatusBar->SetStatusText(msg); devTree->DeleteAllItems(); devTree->AddRoot(msg); event.Skip(); return; } devTree->DeleteAllItems(); wxTreeItemId devRoot = devTree->AddRoot("Devices"); wxTreeItemId localBranch = devTree->AppendItem(devRoot, "Local"); wxTreeItemId dsBranch = devTree->AppendItem(devRoot, "Local Net"); wxTreeItemId remoteBranch = devTree->AppendItem(devRoot, "Remote"); wxTreeItemId manualBranch = devTree->AppendItem(devRoot, "Manual"); devs[""] = SDREnumerator::enumerate_devices("",true); if (devs[""] != NULL) { for (devs_i = devs[""]->begin(); devs_i != devs[""]->end(); devs_i++) { DeviceConfig *devConfig = nullptr; if ((*devs_i)->isManual()) { std::string devName = "Unknown"; if ((*devs_i)->isAvailable()) { devConfig = wxGetApp().getConfig()->getDevice((*devs_i)->getDeviceId()); devName = devConfig->getDeviceName(); } else { devName = (*devs_i)->getDeviceId(); } devItems[devTree->AppendItem(manualBranch, devName)] = (*devs_i); } else if ((*devs_i)->isRemote()) { devConfig = wxGetApp().getConfig()->getDevice((*devs_i)->getDeviceId()); devItems[devTree->AppendItem(dsBranch, devConfig->getDeviceName())] = (*devs_i); } else { devConfig = wxGetApp().getConfig()->getDevice((*devs_i)->getDeviceId()); devItems[devTree->AppendItem(localBranch, devConfig->getDeviceName())] = (*devs_i); } } } std::vector remotes = SDREnumerator::getRemotes(); std::vector::iterator remoteDevs_i; if (remotes.size()) { for (std::string remote : remotes) { devs[remote] = SDREnumerator::enumerate_devices(remote, true); DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(remote); wxTreeItemId remoteNode = devTree->AppendItem(remoteBranch, devConfig->getDeviceName()); if (devs[remote] != NULL) { for (remoteDevs_i = devs[remote]->begin(); remoteDevs_i != devs[remote]->end(); remoteDevs_i++) { devItems[devTree->AppendItem(remoteNode, (*remoteDevs_i)->getName())] = (*remoteDevs_i); } } } } m_refreshButton->Enable(); m_addRemoteButton->Enable(); m_useSelectedButton->Enable(); devTree->Enable(); devTree->ExpandAll(); devStatusBar->SetStatusText("Ready."); refresh = false; } } void SDRDevicesDialog::OnRefreshDevices( wxMouseEvent& /* event */) { doRefreshDevices(); } void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) { if (event.GetProperty() == devSettings["name"]) { DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); wxString devName = event.GetPropertyValue().GetString(); devConfig->setDeviceName(devName.ToStdString()); if (editId) { devTree->SetItemText(editId, devConfig->getDeviceName()); } if (devName == "") { event.GetProperty()->SetValueFromString(devConfig->getDeviceName()); } } else if (dev && event.GetProperty() == devSettings["offset"]) { DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); long offset = event.GetPropertyValue().GetInteger(); devConfig->setOffset(offset); if (dev->isActive() || !wxGetApp().getDevice()) { wxGetApp().setOffset(offset); } } else if (dev && event.GetProperty() == devSettings["sample_rate"]) { DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); std::string strRate = deviceArgs["sample_rate"].options[event.GetPropertyValue().GetInteger()]; long srate = 0; try { srate = std::stol(strRate); devConfig->setSampleRate(srate); if (dev->isActive() || !wxGetApp().getDevice()) { wxGetApp().setSampleRate(srate); } } catch (std::invalid_argument e) { // nop } } else if (dev && event.GetProperty() == devSettings["antenna"]) { DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); std::string strAntennaName = deviceArgs["antenna"].options[event.GetPropertyValue().GetInteger()]; try { devConfig->setAntennaName(strAntennaName); if (dev->isActive() || !wxGetApp().getDevice()) { wxGetApp().setAntennaName(strAntennaName); } } catch (std::invalid_argument e) { // nop } } else if (dev) { wxPGProperty *prop = event.GetProperty(); //change value of RuntimeProps for (std::map::iterator rtp = runtimeProps.begin(); rtp != runtimeProps.end(); rtp++) { if (rtp->second == prop) { SoapySDR::Device *soapyDev = dev->getSoapyDevice(); std::string settingValue = prop->GetValueAsString().ToStdString(); SoapySDR::ArgInfo arg = runtimeArgs[rtp->first]; if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) { settingValue = arg.options[prop->GetChoiceSelection()]; } else if (arg.type == SoapySDR::ArgInfo::BOOL) { settingValue = (prop->GetValueAsString()=="True")?"true":"false"; } else { settingValue = prop->GetValueAsString(); } soapyDev->writeSetting(rtp->first, settingValue); if (dev->isActive()) { wxGetApp().getSDRThread()->writeSetting(rtp->first, settingValue); } return; } } } } void SDRDevicesDialog::OnPropGridFocus( wxFocusEvent& /* event */) { editId = selId; } void SDRDevicesDialog::doRefreshDevices() { selId = nullptr; editId = nullptr; removeId = nullptr; dev = nullptr; wxGetApp().stopDevice(false, 2000); devTree->DeleteAllItems(); devTree->Disable(); m_propertyGrid->Clear(); devSettings.clear(); runtimeArgs.clear(); runtimeProps.clear(); streamProps.clear(); m_refreshButton->Disable(); m_addRemoteButton->Disable(); m_useSelectedButton->Disable(); wxGetApp().reEnumerateDevices(); refresh = true; m_addRemoteButton->SetLabel("Add"); } CubicSDR-0.2.3/src/forms/SDRDevices/SDRDevices.fbp000066400000000000000000002414121322677621400214240ustar00rootroot00000000000000 C++ 1 source_name 0 0 res UTF-8 connect SDRDevicesForm 1000 none 0 SDRDevicesForm . 1 1 1 1 UI 0 0 0 wxAUI_MGR_DEFAULT wxBOTH 1 1 impl_virtual 0 wxID_ANY devFrame 700,467 wxDEFAULT_FRAME_STYLE CubicSDR :: SDR Devices wxTAB_TRAVERSAL 1 OnClose 1 1 1 0 wxID_ANY devStatusBar protected wxST_SIZEGRIP devFrameSizer wxVERTICAL none 5 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel3 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer4 wxHORIZONTAL none 5 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel6 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer6 wxVERTICAL none 5 wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 0 1 0 0 wxID_ANY 0 0 1 devTree 1 protected 1 Resizable 1 wxTR_DEFAULT_STYLE 0 OnTreeDoubleClick OnDeleteItem OnSelectionChanged 5 wxEXPAND 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel4 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer5 wxHORIZONTAL none 5 wxALL 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Refresh 0 0 1 m_refreshButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator OnRefreshDevices 5 wxALL 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Add 0 0 1 m_addRemoteButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator OnAddRemote 5 wxALL|wxALIGN_CENTER_VERTICAL 1 1 1 1 1 1 0 1 1 0 0 Dock 0 Left 1 1 0 0 wxID_ANY Start 0 0 1 m_useSelectedButton 1 protected 1 Resizable 1 0 wxFILTER_NONE wxDefaultValidator OnUseSelected 5 wxEXPAND | wxALL 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 0 0 1 m_panel61 1 protected 1 Resizable 1 0 wxTAB_TRAVERSAL bSizer7 wxVERTICAL none 5 wxALL 0 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY SoapySDR Device Options 0 0 1 m_staticText1 1 protected 1 Resizable 1 0 -1 5 wxALL|wxEXPAND 1 1 1 1 1 1 0 1 1 0 Dock 0 Left 1 1 0 0 wxID_ANY 1 0 0 1 m_propertyGrid 1 protected 1 Resizable 1 wxPG_DEFAULT_STYLE 0 OnPropGridChanged OnPropGridFocus 0 wxID_ANY m_deviceTimer 0 5000 protected OnDeviceTimer CubicSDR-0.2.3/src/forms/SDRDevices/SDRDevices.h000066400000000000000000000032621322677621400211030ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include "SDRDevicesForm.h" #include "SoapySDRThread.h" #include "SDREnumerator.h" #include "SDRDeviceAdd.h" class SDRDevicesDialog: public devFrame { public: SDRDevicesDialog( wxWindow* parent ); void OnClose( wxCloseEvent& event ); void OnDeleteItem( wxTreeEvent& event ); void OnSelectionChanged( wxTreeEvent& event ); void OnAddRemote( wxMouseEvent& event ); void OnUseSelected( wxMouseEvent& event ); void OnTreeDoubleClick( wxMouseEvent& event ); void OnDeviceTimer( wxTimerEvent& event ); void OnRefreshDevices( wxMouseEvent& event ); void OnPropGridChanged( wxPropertyGridEvent& event ); void OnPropGridFocus( wxFocusEvent& event ); private: void refreshDeviceProperties(); void doRefreshDevices(); SDRDeviceInfo *getSelectedDevice(wxTreeItemId selId); wxPGProperty *addArgInfoProperty(wxPropertyGrid *pg, SoapySDR::ArgInfo arg); bool refresh, failed; std::map* > devs; std::vector::iterator devs_i; std::map devItems; std::map::iterator devItems_i; SDRDeviceInfo *dev; std::map deviceArgs; std::map runtimeProps; std::map runtimeArgs; std::map streamProps; std::map devSettings; wxTreeItemId selId; wxTreeItemId editId; wxTreeItemId removeId; SDRDeviceAddDialog *devAddDialog; };CubicSDR-0.2.3/src/forms/SDRDevices/SDRDevicesForm.cpp000066400000000000000000000120361322677621400222610ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "SDRDevicesForm.h" /////////////////////////////////////////////////////////////////////////// devFrame::devFrame(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) { this->SetSizeHints(wxDefaultSize, wxDefaultSize); devStatusBar = this->CreateStatusBar(1, wxST_SIZEGRIP, wxID_ANY); wxBoxSizer* devFrameSizer; devFrameSizer = new wxBoxSizer(wxVERTICAL); m_panel3 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); wxBoxSizer* bSizer4; bSizer4 = new wxBoxSizer(wxHORIZONTAL); m_panel6 = new wxPanel(m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); wxBoxSizer* bSizer6; bSizer6 = new wxBoxSizer(wxVERTICAL); devTree = new wxTreeCtrl(m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE); devTree->Enable(false); bSizer6->Add(devTree, 1, wxEXPAND, 5); m_panel4 = new wxPanel(m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); wxBoxSizer* bSizer5; bSizer5 = new wxBoxSizer(wxHORIZONTAL); m_refreshButton = new wxButton(m_panel4, wxID_ANY, wxT("Refresh"), wxDefaultPosition, wxDefaultSize, 0); bSizer5->Add(m_refreshButton, 1, wxALL, 5); m_addRemoteButton = new wxButton(m_panel4, wxID_ANY, wxT("Add"), wxDefaultPosition, wxDefaultSize, 0); bSizer5->Add(m_addRemoteButton, 1, wxALL, 5); m_useSelectedButton = new wxButton(m_panel4, wxID_ANY, wxT("Start"), wxDefaultPosition, wxDefaultSize, 0); bSizer5->Add(m_useSelectedButton, 1, wxALL | wxALIGN_CENTER_VERTICAL, 5); m_panel4->SetSizer(bSizer5); m_panel4->Layout(); bSizer5->Fit(m_panel4); bSizer6->Add(m_panel4, 0, wxEXPAND, 5); m_panel6->SetSizer(bSizer6); m_panel6->Layout(); bSizer6->Fit(m_panel6); bSizer4->Add(m_panel6, 1, wxEXPAND | wxALL, 5); m_panel61 = new wxPanel(m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); wxBoxSizer* bSizer7; bSizer7 = new wxBoxSizer(wxVERTICAL); m_staticText1 = new wxStaticText(m_panel61, wxID_ANY, wxT("SoapySDR Device Options"), wxDefaultPosition, wxDefaultSize, 0); m_staticText1->Wrap(-1); bSizer7->Add(m_staticText1, 0, wxALL, 5); m_propertyGrid = new wxPropertyGrid(m_panel61, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_DEFAULT_STYLE); bSizer7->Add(m_propertyGrid, 1, wxALL | wxEXPAND, 5); m_panel61->SetSizer(bSizer7); m_panel61->Layout(); bSizer7->Fit(m_panel61); bSizer4->Add(m_panel61, 1, wxEXPAND | wxALL, 5); m_panel3->SetSizer(bSizer4); m_panel3->Layout(); bSizer4->Fit(m_panel3); devFrameSizer->Add(m_panel3, 1, wxEXPAND | wxALL, 5); this->SetSizer(devFrameSizer); this->Layout(); m_deviceTimer.SetOwner(this, wxID_ANY); this->Centre(wxBOTH); // Connect Events this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(devFrame::OnClose)); devTree->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(devFrame::OnTreeDoubleClick), NULL, this); devTree->Connect(wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler(devFrame::OnDeleteItem), NULL, this); devTree->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(devFrame::OnSelectionChanged), NULL, this); m_refreshButton->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnRefreshDevices), NULL, this); m_addRemoteButton->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnAddRemote), NULL, this); m_useSelectedButton->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnUseSelected), NULL, this); m_propertyGrid->Connect(wxEVT_PG_CHANGED, wxPropertyGridEventHandler(devFrame::OnPropGridChanged), NULL, this); m_propertyGrid->Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(devFrame::OnPropGridFocus), NULL, this); this->Connect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(devFrame::OnDeviceTimer)); } devFrame::~devFrame() { // Disconnect Events this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(devFrame::OnClose)); devTree->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(devFrame::OnTreeDoubleClick), NULL, this); devTree->Disconnect(wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler(devFrame::OnDeleteItem), NULL, this); devTree->Disconnect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(devFrame::OnSelectionChanged), NULL, this); m_refreshButton->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnRefreshDevices), NULL, this); m_addRemoteButton->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnAddRemote), NULL, this); m_useSelectedButton->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnUseSelected), NULL, this); m_propertyGrid->Disconnect(wxEVT_PG_CHANGED, wxPropertyGridEventHandler(devFrame::OnPropGridChanged), NULL, this); m_propertyGrid->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(devFrame::OnPropGridFocus), NULL, this); this->Disconnect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(devFrame::OnDeviceTimer)); } CubicSDR-0.2.3/src/forms/SDRDevices/SDRDevicesForm.h000066400000000000000000000047121322677621400217300ustar00rootroot00000000000000/////////////////////////////////////////////////////////////////////////// // C++ code generated with wxFormBuilder (version Oct 27 2017) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #ifndef __SDRDEVICESFORM_H__ #define __SDRDEVICESFORM_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Class devFrame /////////////////////////////////////////////////////////////////////////////// class devFrame : public wxFrame { private: protected: wxStatusBar* devStatusBar; wxPanel* m_panel3; wxPanel* m_panel6; wxTreeCtrl* devTree; wxPanel* m_panel4; wxButton* m_refreshButton; wxButton* m_addRemoteButton; wxButton* m_useSelectedButton; wxPanel* m_panel61; wxStaticText* m_staticText1; wxPropertyGrid* m_propertyGrid; wxTimer m_deviceTimer; // Virtual event handlers, overide them in your derived class virtual void OnClose(wxCloseEvent& event) { event.Skip(); } virtual void OnTreeDoubleClick(wxMouseEvent& event) { event.Skip(); } virtual void OnDeleteItem(wxTreeEvent& event) { event.Skip(); } virtual void OnSelectionChanged(wxTreeEvent& event) { event.Skip(); } virtual void OnRefreshDevices(wxMouseEvent& event) { event.Skip(); } virtual void OnAddRemote(wxMouseEvent& event) { event.Skip(); } virtual void OnUseSelected(wxMouseEvent& event) { event.Skip(); } virtual void OnPropGridChanged(wxPropertyGridEvent& event) { event.Skip(); } virtual void OnPropGridFocus(wxFocusEvent& event) { event.Skip(); } virtual void OnDeviceTimer(wxTimerEvent& event) { event.Skip(); } public: devFrame(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("CubicSDR :: SDR Devices"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(700, 467), long style = wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL); ~devFrame(); }; #endif //__SDRDEVICESFORM_H__ CubicSDR-0.2.3/src/modules/000077500000000000000000000000001322677621400153635ustar00rootroot00000000000000CubicSDR-0.2.3/src/modules/modem/000077500000000000000000000000001322677621400164645ustar00rootroot00000000000000CubicSDR-0.2.3/src/modules/modem/Modem.cpp000066400000000000000000000047251322677621400202410ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "Modem.h" #include "CubicSDR.h" ModemFactoryList Modem::modemFactories; DefaultRatesList Modem::modemDefaultRates; //! Create an empty range (0.0, 0.0) ModemRange::ModemRange(void) { _min = 0; _max = 0; } //! Create a min/max range ModemRange::ModemRange(const double minimum, const double maximum) { _min = minimum; _max = maximum; } //! Get the range minimum double ModemRange::minimum(void) const { return _min; } //! Get the range maximum double ModemRange::maximum(void) const { return _max; } ModemArgInfo::ModemArgInfo(void) { } Modem::Modem() { useSignalOutput(false); } Modem::~Modem() { } void Modem::addModemFactory(ModemFactoryFn factoryFunc, std::string modemName, int defaultRate) { modemFactories[modemName] = factoryFunc; modemDefaultRates[modemName] = defaultRate; } ModemFactoryList Modem::getFactories() { return modemFactories; } Modem *Modem::makeModem(std::string modemName) { if (modemFactories.find(modemName) != modemFactories.end()) { return (Modem *)modemFactories[modemName](); } return nullptr; } int Modem::getModemDefaultSampleRate(std::string modemName) { if (modemDefaultRates.find(modemName) != modemDefaultRates.end()) { return modemDefaultRates[modemName]; } return 0; } ModemArgInfoList Modem::getSettings() { ModemArgInfoList args; return args; } int Modem::getDefaultSampleRate() { return 200000; } void Modem::writeSetting(std::string /* setting */, std::string /* value */) { // ... } std::string Modem::readSetting(std::string /* setting */) { return ""; } void Modem::writeSettings(ModemSettings settings) { for (ModemSettings::const_iterator i = settings.begin(); i != settings.end(); i++) { writeSetting(i->first, i->second); } } ModemSettings Modem::readSettings() { ModemArgInfoList args = getSettings(); ModemSettings rs; for (ModemArgInfoList::const_iterator i = args.begin(); i != args.end(); i++) { rs[i->key] = readSetting(i->key); } return rs; } bool Modem::shouldRebuildKit() { return refreshKit.load(); } void Modem::rebuildKit() { refreshKit.store(true); } void Modem::clearRebuildKit() { refreshKit.store(false); } bool Modem::useSignalOutput() { return _useSignalOutput.load(); } void Modem::useSignalOutput(bool useOutput) { _useSignalOutput.store(useOutput); } CubicSDR-0.2.3/src/modules/modem/Modem.h000066400000000000000000000103721322677621400177010ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "liquid/liquid.h" #include "IOThread.h" #include "AudioThread.h" #include #include #include #define MIN_BANDWIDTH 500 #ifndef M_PI #define M_PI 3.14159265358979323846 #endif class ModemKit { public: ModemKit() : sampleRate(0), audioSampleRate(0) { } long long sampleRate; int audioSampleRate; }; class ModemIQData { public: std::vector data; long long sampleRate; ModemIQData() : sampleRate(0) { } virtual ~ModemIQData() { } }; typedef std::shared_ptr ModemIQDataPtr; // Copy of SoapySDR::Range, original comments class ModemRange { public: //! Create an empty range (0.0, 0.0) ModemRange(void); //! Create a min/max range ModemRange(const double minimum, const double maximum); //! Get the range minimum double minimum(void) const; //! Get the range maximum double maximum(void) const; private: double _min, _max; }; // Modified version of SoapySDR::ArgInfo, original comments class ModemArgInfo { public: //! Default constructor ModemArgInfo(void); //! The key used to identify the argument (required) std::string key; /*! * The default value of the argument when not specified (required) * Numbers should use standard floating point and integer formats. * Boolean values should be represented as "true" and "false". */ std::string value; //! The displayable name of the argument (optional, use key if empty) std::string name; //! A brief description about the argument (optional) std::string description; //! The units of the argument: dB, Hz, etc (optional) std::string units; //! The data type of the argument (required) enum Type { BOOL, INT, FLOAT, STRING, PATH_DIR, PATH_FILE, COLOR } type; /*! * The range of possible numeric values (optional) * When specified, the argument should be restricted to this range. * The range is only applicable to numeric argument types. */ ModemRange range; /*! * A discrete list of possible values (optional) * When specified, the argument should be restricted to this options set. */ std::vector options; /*! * A discrete list of displayable names for the enumerated options (optional) * When not specified, the option value itself can be used as a display name. */ std::vector optionNames; }; typedef std::vector ModemArgInfoList; class ModemBase { }; typedef ModemBase *(*ModemFactoryFn)(); typedef std::map ModemFactoryList; typedef std::map DefaultRatesList; typedef std::map ModemSettings; class Modem : public ModemBase { public: static void addModemFactory(ModemFactoryFn, std::string modemName, int defaultRate); static ModemFactoryList getFactories(); static Modem *makeModem(std::string modemName); static int getModemDefaultSampleRate(std::string modemName); virtual std::string getType() = 0; virtual std::string getName() = 0; Modem(); virtual ~Modem(); virtual ModemArgInfoList getSettings(); virtual int getDefaultSampleRate(); virtual void writeSetting(std::string setting, std::string value); virtual void writeSettings(ModemSettings settings); virtual std::string readSetting(std::string setting); virtual ModemSettings readSettings(); virtual int checkSampleRate(long long sampleRate, int audioSampleRate) = 0; virtual ModemKit *buildKit(long long sampleRate, int audioSampleRate) = 0; virtual void disposeKit(ModemKit *kit) = 0; virtual void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) = 0; bool shouldRebuildKit(); void rebuildKit(); void clearRebuildKit(); bool useSignalOutput(); void useSignalOutput(bool useOutput); private: static ModemFactoryList modemFactories; static DefaultRatesList modemDefaultRates; std::atomic_bool refreshKit, _useSignalOutput; }; CubicSDR-0.2.3/src/modules/modem/ModemAnalog.cpp000066400000000000000000000057701322677621400213640ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemAnalog.h" ModemAnalog::ModemAnalog() : Modem(), aOutputCeil(1), aOutputCeilMA(1), aOutputCeilMAA(1) { } std::string ModemAnalog::getType() { return "analog"; } int ModemAnalog::checkSampleRate(long long sampleRate, int /* audioSampleRate */) { if (sampleRate < MIN_BANDWIDTH) { return MIN_BANDWIDTH; } return (int)sampleRate; } ModemKit *ModemAnalog::buildKit(long long sampleRate, int audioSampleRate) { ModemKitAnalog *akit = new ModemKitAnalog; // stop-band attenuation [dB] float As = 60.0f; akit->sampleRate = sampleRate; akit->audioSampleRate = audioSampleRate; akit->audioResampleRatio = double(audioSampleRate) / double(sampleRate); akit->audioResampler = msresamp_rrrf_create((float)akit->audioResampleRatio, As); return akit; } void ModemAnalog::disposeKit(ModemKit *kit) { ModemKitAnalog *akit = (ModemKitAnalog *)kit; msresamp_rrrf_destroy(akit->audioResampler); delete akit; } void ModemAnalog::initOutputBuffers(ModemKitAnalog *akit, ModemIQData *input) { bufSize = input->data.size(); if (!bufSize) { return; } double audio_resample_ratio = akit->audioResampleRatio; size_t audio_out_size = (size_t)ceil((double) (bufSize) * audio_resample_ratio) + 512; if (demodOutputData.size() != bufSize) { if (demodOutputData.capacity() < bufSize) { demodOutputData.reserve(bufSize); } demodOutputData.resize(bufSize); } if (resampledOutputData.size() != audio_out_size) { if (resampledOutputData.capacity() < audio_out_size) { resampledOutputData.reserve(audio_out_size); } resampledOutputData.resize(audio_out_size); } } void ModemAnalog::buildAudioOutput(ModemKitAnalog *akit, AudioThreadInput *audioOut, bool autoGain) { unsigned int numAudioWritten; if (autoGain) { aOutputCeilMA = aOutputCeilMA + (aOutputCeil - aOutputCeilMA) * 0.025f; aOutputCeilMAA = aOutputCeilMAA + (aOutputCeilMA - aOutputCeilMAA) * 0.025f; aOutputCeil = 0; for (size_t i = 0; i < bufSize; i++) { if (demodOutputData[i] > aOutputCeil) { aOutputCeil = demodOutputData[i]; } } float gain = 0.5f / aOutputCeilMAA; for (size_t i = 0; i < bufSize; i++) { demodOutputData[i] *= gain; } } msresamp_rrrf_execute(akit->audioResampler, &demodOutputData[0], (int)demodOutputData.size(), &resampledOutputData[0], &numAudioWritten); audioOut->channels = 1; audioOut->sampleRate = akit->audioSampleRate; audioOut->data.assign(resampledOutputData.begin(), resampledOutputData.begin() + numAudioWritten); } std::vector *ModemAnalog::getDemodOutputData() { return &demodOutputData; } std::vector *ModemAnalog::getResampledOutputData() { return &resampledOutputData; } CubicSDR-0.2.3/src/modules/modem/ModemAnalog.h000066400000000000000000000021111322677621400210130ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Modem.h" class ModemKitAnalog : public ModemKit { public: ModemKitAnalog() : ModemKit(), audioResampler(nullptr), audioResampleRatio(0) { }; msresamp_rrrf audioResampler; double audioResampleRatio; }; class ModemAnalog : public Modem { public: ModemAnalog(); std::string getType(); virtual int checkSampleRate(long long sampleRate, int audioSampleRate); virtual ModemKit *buildKit(long long sampleRate, int audioSampleRate); virtual void disposeKit(ModemKit *kit); virtual void initOutputBuffers(ModemKitAnalog *akit, ModemIQData *input); virtual void buildAudioOutput(ModemKitAnalog *akit, AudioThreadInput *audioOut, bool autoGain); virtual std::vector *getDemodOutputData(); virtual std::vector *getResampledOutputData(); protected: size_t bufSize; std::vector demodOutputData; std::vector resampledOutputData; float aOutputCeil; float aOutputCeilMA; float aOutputCeilMAA; }; CubicSDR-0.2.3/src/modules/modem/ModemDigital.cpp000066400000000000000000000040011322677621400215220ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemDigital.h" ModemDigitalOutput::ModemDigitalOutput() { } ModemDigital::ModemDigital() : Modem() { #if ENABLE_DIGITAL_LAB digitalOut = nullptr; #endif } ModemDigitalOutput::~ModemDigitalOutput() { } std::string ModemDigital::getType() { return "digital"; } int ModemDigital::checkSampleRate(long long sampleRate, int /* audioSampleRate */) { if (sampleRate < MIN_BANDWIDTH) { return MIN_BANDWIDTH; } return (int)sampleRate; } ModemKit *ModemDigital::buildKit(long long sampleRate, int audioSampleRate) { ModemKitDigital *dkit = new ModemKitDigital; dkit->sampleRate = sampleRate; dkit->audioSampleRate = audioSampleRate; return dkit; } void ModemDigital::disposeKit(ModemKit *kit) { ModemKitDigital *dkit = (ModemKitDigital *)kit; delete dkit; } void ModemDigital::setDemodulatorLock(bool demod_lock_in) { currentDemodLock.store(demod_lock_in); } int ModemDigital::getDemodulatorLock() { return currentDemodLock.load(); } void ModemDigital::updateDemodulatorLock(modem mod, float sensitivity) { setDemodulatorLock(modem_get_demodulator_evm(mod) <= sensitivity); } void ModemDigital::digitalStart(ModemKitDigital * /* kit */, modem /* mod */, ModemIQData *input) { size_t bufSize = input->data.size(); if (demodOutputDataDigital.size() != bufSize) { if (demodOutputDataDigital.capacity() < bufSize) { demodOutputDataDigital.reserve(bufSize); } demodOutputDataDigital.resize(bufSize); } } void ModemDigital::digitalFinish(ModemKitDigital * /* kit */, modem /* mod */) { #if ENABLE_DIGITAL_LAB if (digitalOut && outStream.str().length()) { digitalOut->write(outStream.str()); outStream.str(""); } else { outStream.str(""); } #endif } #if ENABLE_DIGITAL_LAB void ModemDigital::setOutput(ModemDigitalOutput *modemDigitalOutput) { digitalOut = modemDigitalOutput; } #endif CubicSDR-0.2.3/src/modules/modem/ModemDigital.h000066400000000000000000000027601322677621400212010ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Modem.h" #include #include #include #include #include class ModemKitDigital : public ModemKit { public: ModemKitDigital() : ModemKit() { }; }; class ModemDigitalOutput { public: ModemDigitalOutput(); virtual ~ModemDigitalOutput(); virtual void write(std::string outp) = 0; virtual void write(char outc) = 0; virtual void Show() = 0; virtual void Hide() = 0; virtual void Close() = 0; private: }; class ModemDigital : public Modem { public: ModemDigital(); std::string getType(); virtual int checkSampleRate(long long sampleRate, int audioSampleRate); virtual ModemKit *buildKit(long long sampleRate, int audioSampleRate); virtual void disposeKit(ModemKit *kit); virtual void digitalStart(ModemKitDigital *kit, modem mod, ModemIQData *input); virtual void digitalFinish(ModemKitDigital *kit, modem mod); virtual void setDemodulatorLock(bool demod_lock_in); virtual int getDemodulatorLock(); virtual void updateDemodulatorLock(modem mod, float sensitivity); #if ENABLE_DIGITAL_LAB void setOutput(ModemDigitalOutput *digitalOutput); #endif protected: std::vector demodOutputDataDigital; std::atomic_bool currentDemodLock; #if ENABLE_DIGITAL_LAB ModemDigitalOutput *digitalOut; std::stringstream outStream; #endif };CubicSDR-0.2.3/src/modules/modem/analog/000077500000000000000000000000001322677621400177255ustar00rootroot00000000000000CubicSDR-0.2.3/src/modules/modem/analog/ModemAM.cpp000066400000000000000000000015311322677621400217100ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemAM.h" ModemAM::ModemAM() : ModemAnalog() { demodAM = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_DSB, 0); useSignalOutput(true); } ModemAM::~ModemAM() { ampmodem_destroy(demodAM); } ModemBase *ModemAM::factory() { return new ModemAM; } std::string ModemAM::getName() { return "AM"; } int ModemAM::getDefaultSampleRate() { return 6000; } void ModemAM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput* audioOut) { ModemKitAnalog *amkit = (ModemKitAnalog *)kit; initOutputBuffers(amkit,input); if (!bufSize) { return; } for (size_t i = 0; i < bufSize; i++) { ampmodem_demodulate(demodAM, input->data[i], &demodOutputData[i]); } buildAudioOutput(amkit,audioOut,true); } CubicSDR-0.2.3/src/modules/modem/analog/ModemAM.h000066400000000000000000000006651322677621400213640ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Modem.h" #include "ModemAnalog.h" class ModemAM : public ModemAnalog { public: ModemAM(); ~ModemAM(); std::string getName(); static ModemBase *factory(); int getDefaultSampleRate(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: ampmodem demodAM; };CubicSDR-0.2.3/src/modules/modem/analog/ModemDSB.cpp000066400000000000000000000015571322677621400220330ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemDSB.h" ModemDSB::ModemDSB() : ModemAnalog() { demodAM_DSB = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_DSB, 1); useSignalOutput(true); } ModemDSB::~ModemDSB() { ampmodem_destroy(demodAM_DSB); } ModemBase *ModemDSB::factory() { return new ModemDSB; } std::string ModemDSB::getName() { return "DSB"; } int ModemDSB::getDefaultSampleRate() { return 5400; } void ModemDSB::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitAnalog *amkit = (ModemKitAnalog *)kit; initOutputBuffers(amkit, input); if (!bufSize) { return; } for (size_t i = 0; i < bufSize; i++) { ampmodem_demodulate(demodAM_DSB, input->data[i], &demodOutputData[i]); } buildAudioOutput(amkit, audioOut, true); } CubicSDR-0.2.3/src/modules/modem/analog/ModemDSB.h000066400000000000000000000006741322677621400214770ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Modem.h" #include "ModemAnalog.h" class ModemDSB : public ModemAnalog { public: ModemDSB(); ~ModemDSB(); std::string getName(); static ModemBase *factory(); int getDefaultSampleRate(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: ampmodem demodAM_DSB; };CubicSDR-0.2.3/src/modules/modem/analog/ModemFM.cpp000066400000000000000000000014131322677621400217140ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemFM.h" ModemFM::ModemFM() : ModemAnalog() { demodFM = freqdem_create(0.5); } ModemFM::~ModemFM() { freqdem_destroy(demodFM); } ModemBase *ModemFM::factory() { return new ModemFM; } std::string ModemFM::getName() { return "FM"; } int ModemFM::getDefaultSampleRate() { return 200000; } void ModemFM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitAnalog *fmkit = (ModemKitAnalog *)kit; initOutputBuffers(fmkit, input); if (!bufSize) { return; } freqdem_demodulate_block(demodFM, &input->data[0], (int)bufSize, &demodOutputData[0]); buildAudioOutput(fmkit, audioOut, false); } CubicSDR-0.2.3/src/modules/modem/analog/ModemFM.h000066400000000000000000000006541322677621400213670ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Modem.h" #include "ModemAnalog.h" class ModemFM : public ModemAnalog { public: ModemFM(); ~ModemFM(); std::string getName(); static ModemBase *factory(); int getDefaultSampleRate(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: freqdem demodFM; };CubicSDR-0.2.3/src/modules/modem/analog/ModemFMStereo.cpp000066400000000000000000000225001322677621400230760ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemFMStereo.h" ModemFMStereo::ModemFMStereo() { demodFM = freqdem_create(0.5); _demph = 75; } ModemFMStereo::~ModemFMStereo() { freqdem_destroy(demodFM); } std::string ModemFMStereo::getType() { return "analog"; } std::string ModemFMStereo::getName() { return "FMS"; } ModemBase *ModemFMStereo::factory() { return new ModemFMStereo; } int ModemFMStereo::checkSampleRate(long long sampleRate, int /* audioSampleRate */) { if (sampleRate < 100000) { return 100000; } else if (sampleRate < 1500) { return 1500; } else { return (int)sampleRate; } } int ModemFMStereo::getDefaultSampleRate() { return 200000; } ModemArgInfoList ModemFMStereo::getSettings() { ModemArgInfoList args; ModemArgInfo demphArg; demphArg.key = "demph"; demphArg.name = "De-emphasis"; demphArg.value = std::to_string(_demph); demphArg.description = "FM Stereo De-Emphasis, typically 75us in US/Canada, 50us elsewhere."; demphArg.type = ModemArgInfo::STRING; std::vector demphOptNames; demphOptNames.push_back("None"); demphOptNames.push_back("10us"); demphOptNames.push_back("25us"); demphOptNames.push_back("32us"); demphOptNames.push_back("50us"); demphOptNames.push_back("75us"); demphArg.optionNames = demphOptNames; std::vector demphOpts; demphOpts.push_back("0"); demphOpts.push_back("10"); demphOpts.push_back("25"); demphOpts.push_back("32"); demphOpts.push_back("50"); demphOpts.push_back("75"); demphArg.options = demphOpts; args.push_back(demphArg); return args; } void ModemFMStereo::writeSetting(std::string setting, std::string value) { if (setting == "demph") { _demph = std::stoi(value); rebuildKit(); } } std::string ModemFMStereo::readSetting(std::string setting) { if (setting == "demph") { return std::to_string(_demph); } return ""; } ModemKit *ModemFMStereo::buildKit(long long sampleRate, int audioSampleRate) { ModemKitFMStereo *kit = new ModemKitFMStereo; kit->audioResampleRatio = double(audioSampleRate) / double(sampleRate); kit->sampleRate = sampleRate; kit->audioSampleRate = audioSampleRate; float As = 60.0f; // stop-band attenuation [dB] kit->audioResampler = msresamp_rrrf_create((float)kit->audioResampleRatio, As); kit->stereoResampler = msresamp_rrrf_create((float)kit->audioResampleRatio, As); // Stereo filters / shifters float firStereoCutoff = 16000.0f / float(audioSampleRate); // filter transition float ft = 1000.0f / float(audioSampleRate); // fractional timing offset float mu = 0.0f; if (firStereoCutoff < 0) { firStereoCutoff = 0; } if (firStereoCutoff > 0.5) { firStereoCutoff = 0.5; } unsigned int h_len = estimate_req_filter_len(ft, As); float *h = new float[h_len]; liquid_firdes_kaiser(h_len, firStereoCutoff, As, mu, h); kit->firStereoLeft = firfilt_rrrf_create(h, h_len); kit->firStereoRight = firfilt_rrrf_create(h, h_len); // stereo pilot filter float bw = float(sampleRate); if (bw < 100000.0) { bw = 100000.0; } unsigned int order = 5; // filter order float f0 = ((float) 19000 / bw); float fc = ((float) 19500 / bw); float Ap = 1.0f; kit->iirStereoPilot = iirfilt_crcf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_BANDPASS, LIQUID_IIRDES_SOS, order, fc, f0, Ap, As); kit->firStereoR2C = firhilbf_create(5, 60.0f); kit->firStereoC2R = firhilbf_create(5, 60.0f); kit->stereoPilot = nco_crcf_create(LIQUID_VCO); nco_crcf_reset(kit->stereoPilot); nco_crcf_pll_set_bandwidth(kit->stereoPilot, 0.25f); kit->demph = _demph; if (_demph) { double f = (1.0 / (2.0 * M_PI * double(_demph) * 1e-6)); double t = 1.0 / (2.0 * M_PI * f); t = 1.0 / (2.0 * double(audioSampleRate) * tan(1.0 / (2.0 * double(audioSampleRate) * t))); double tb = (1.0 + 2.0 * t * double(audioSampleRate)); float b_demph[2] = { (float)(1.0 / tb), (float)(1.0 / tb) }; float a_demph[2] = { 1.0, (float)((1.0 - 2.0 * t * double(audioSampleRate)) / tb) }; kit->iirDemphL = iirfilt_rrrf_create(b_demph, 2, a_demph, 2); kit->iirDemphR = iirfilt_rrrf_create(b_demph, 2, a_demph, 2); } else { kit->iirDemphL = nullptr; kit->iirDemphR = nullptr; kit->demph = 0; } return kit; } void ModemFMStereo::disposeKit(ModemKit *kit) { ModemKitFMStereo *fmkit = (ModemKitFMStereo *)kit; msresamp_rrrf_destroy(fmkit->audioResampler); msresamp_rrrf_destroy(fmkit->stereoResampler); firfilt_rrrf_destroy(fmkit->firStereoLeft); firfilt_rrrf_destroy(fmkit->firStereoRight); firhilbf_destroy(fmkit->firStereoR2C); firhilbf_destroy(fmkit->firStereoC2R); nco_crcf_destroy(fmkit->stereoPilot); if (fmkit->iirDemphR) { iirfilt_rrrf_destroy(fmkit->iirDemphR); } if (fmkit->iirDemphL) { iirfilt_rrrf_destroy(fmkit->iirDemphL); } } void ModemFMStereo::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitFMStereo *fmkit = (ModemKitFMStereo *)kit; size_t bufSize = input->data.size(); liquid_float_complex u, v, w, x, y; double audio_resample_ratio = fmkit->audioResampleRatio; if (demodOutputData.size() != bufSize) { if (demodOutputData.capacity() < bufSize) { demodOutputData.reserve(bufSize); } demodOutputData.resize(bufSize); } size_t audio_out_size = (size_t)ceil((double) (bufSize) * audio_resample_ratio) + 512; freqdem_demodulate_block(demodFM, &input->data[0], (int)bufSize, &demodOutputData[0]); if (resampledOutputData.size() != audio_out_size) { if (resampledOutputData.capacity() < audio_out_size) { resampledOutputData.reserve(audio_out_size); } resampledOutputData.resize(audio_out_size); } unsigned int numAudioWritten; msresamp_rrrf_execute(fmkit->audioResampler, &demodOutputData[0], (int)bufSize, &resampledOutputData[0], &numAudioWritten); if (demodStereoData.size() != bufSize) { if (demodStereoData.capacity() < bufSize) { demodStereoData.reserve(bufSize); } demodStereoData.resize(bufSize); } float phase_error = 0; for (size_t i = 0; i < bufSize; i++) { // real -> complex firhilbf_r2c_execute(fmkit->firStereoR2C, demodOutputData[i], &x); // 19khz pilot band-pass iirfilt_crcf_execute(fmkit->iirStereoPilot, x, &v); nco_crcf_cexpf(fmkit->stereoPilot, &w); w.imag = -w.imag; // conjf(w) // multiply u = v * conjf(w) u.real = v.real * w.real - v.imag * w.imag; u.imag = v.real * w.imag + v.imag * w.real; // cargf(u) phase_error = atan2f(u.imag,u.real); // step pll nco_crcf_pll_step(fmkit->stereoPilot, phase_error); nco_crcf_step(fmkit->stereoPilot); // 38khz down-mix nco_crcf_mix_down(fmkit->stereoPilot, x, &y); nco_crcf_mix_down(fmkit->stereoPilot, y, &x); // complex -> real firhilbf_c2r_execute(fmkit->firStereoC2R, x, &demodStereoData[i]); } // std::cout << "[PLL] phase error: " << phase_error; // std::cout << " freq:" << (((nco_crcf_get_frequency(stereoPilot) / (2.0 * M_PI)) * inp->sampleRate)) << std::endl; if (audio_out_size != resampledStereoData.size()) { if (resampledStereoData.capacity() < audio_out_size) { resampledStereoData.reserve(audio_out_size); } resampledStereoData.resize(audio_out_size); } msresamp_rrrf_execute(fmkit->stereoResampler, &demodStereoData[0], (int)bufSize, &resampledStereoData[0], &numAudioWritten); audioOut->channels = 2; if (audioOut->data.capacity() < (numAudioWritten * 2)) { audioOut->data.reserve(numAudioWritten * 2); } audioOut->data.resize(numAudioWritten * 2); for (size_t i = 0; i < numAudioWritten; i++) { float l, r; float ld, rd; if (fmkit->demph) { iirfilt_rrrf_execute(fmkit->iirDemphL, 0.568f * (resampledOutputData[i] - (resampledStereoData[i])), &ld); iirfilt_rrrf_execute(fmkit->iirDemphR, 0.568f * (resampledOutputData[i] + (resampledStereoData[i])), &rd); firfilt_rrrf_push(fmkit->firStereoLeft, ld); firfilt_rrrf_execute(fmkit->firStereoLeft, &l); firfilt_rrrf_push(fmkit->firStereoRight, rd); firfilt_rrrf_execute(fmkit->firStereoRight, &r); } else { firfilt_rrrf_push(fmkit->firStereoLeft, 0.568f * (resampledOutputData[i] - (resampledStereoData[i]))); firfilt_rrrf_execute(fmkit->firStereoLeft, &l); firfilt_rrrf_push(fmkit->firStereoRight, 0.568f * (resampledOutputData[i] + (resampledStereoData[i]))); firfilt_rrrf_execute(fmkit->firStereoRight, &r); } audioOut->data[i * 2] = l; audioOut->data[i * 2 + 1] = r; } } CubicSDR-0.2.3/src/modules/modem/analog/ModemFMStereo.h000066400000000000000000000030421322677621400225430ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Modem.h" class ModemKitFMStereo: public ModemKit { public: ModemKitFMStereo() : audioResampler(nullptr), stereoResampler(nullptr), audioResampleRatio(0), firStereoLeft(nullptr), firStereoRight(nullptr), iirStereoPilot(nullptr) { } msresamp_rrrf audioResampler; msresamp_rrrf stereoResampler; double audioResampleRatio; firfilt_rrrf firStereoLeft; firfilt_rrrf firStereoRight; iirfilt_crcf iirStereoPilot; int demph; iirfilt_rrrf iirDemphR; iirfilt_rrrf iirDemphL; firhilbf firStereoR2C; firhilbf firStereoC2R; nco_crcf stereoPilot; }; class ModemFMStereo : public Modem { public: ModemFMStereo(); ~ModemFMStereo(); std::string getType(); std::string getName(); static ModemBase *factory(); int checkSampleRate(long long sampleRate, int audioSampleRate); int getDefaultSampleRate(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); ModemKit *buildKit(long long sampleRate, int audioSampleRate); void disposeKit(ModemKit *kit); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: std::vector demodOutputData; std::vector demodStereoData; std::vector resampledOutputData; std::vector resampledStereoData; freqdem demodFM; int _demph; };CubicSDR-0.2.3/src/modules/modem/analog/ModemIQ.cpp000066400000000000000000000023611322677621400217260ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemIQ.h" ModemIQ::ModemIQ() { } std::string ModemIQ::getType() { return "analog"; } std::string ModemIQ::getName() { return "I/Q"; } ModemBase *ModemIQ::factory() { return new ModemIQ; } ModemKit *ModemIQ::buildKit(long long sampleRate, int audioSampleRate) { ModemKit *kit = new ModemKit; kit->sampleRate = sampleRate; kit->audioSampleRate = audioSampleRate; return kit; } void ModemIQ::disposeKit(ModemKit *kit) { delete kit; } int ModemIQ::checkSampleRate(long long /* sampleRate */, int audioSampleRate) { return audioSampleRate; } int ModemIQ::getDefaultSampleRate() { return 48000; } void ModemIQ::demodulate(ModemKit * /* kit */, ModemIQData *input, AudioThreadInput *audioOut) { size_t bufSize = input->data.size(); if (!bufSize) { return; } audioOut->channels = 2; if (audioOut->data.capacity() < (bufSize * 2)) { audioOut->data.reserve(bufSize * 2); } audioOut->data.resize(bufSize * 2); for (size_t i = 0; i < bufSize; i++) { audioOut->data[i * 2] = input->data[i].imag; audioOut->data[i * 2 + 1] = input->data[i].real; } } CubicSDR-0.2.3/src/modules/modem/analog/ModemIQ.h000066400000000000000000000010711322677621400213700ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Modem.h" class ModemIQ : public Modem { public: ModemIQ(); std::string getType(); std::string getName(); static ModemBase *factory(); int checkSampleRate(long long sampleRate, int audioSampleRate); int getDefaultSampleRate(); ModemKit *buildKit(long long sampleRate, int audioSampleRate); void disposeKit(ModemKit *kit); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: };CubicSDR-0.2.3/src/modules/modem/analog/ModemLSB.cpp000066400000000000000000000031031322677621400220300ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemLSB.h" ModemLSB::ModemLSB() : ModemAnalog() { // half band filter used for side-band elimination ssbFilt = iirfilt_crcf_create_lowpass(6, 0.25); ssbShift = nco_crcf_create(LIQUID_NCO); nco_crcf_set_frequency(ssbShift, (float)((2.0 * M_PI) * 0.25)); c2rFilt = firhilbf_create(5, 90.0); useSignalOutput(true); } ModemBase *ModemLSB::factory() { return new ModemLSB; } std::string ModemLSB::getName() { return "LSB"; } ModemLSB::~ModemLSB() { iirfilt_crcf_destroy(ssbFilt); nco_crcf_destroy(ssbShift); firhilbf_destroy(c2rFilt); } int ModemLSB::checkSampleRate(long long sampleRate, int /* audioSampleRate */) { if (sampleRate < MIN_BANDWIDTH) { return MIN_BANDWIDTH; } if (sampleRate % 2 == 0) { return (int)sampleRate; } return (int)(sampleRate+1); } int ModemLSB::getDefaultSampleRate() { return 5400; } void ModemLSB::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitAnalog *akit = (ModemKitAnalog *)kit; initOutputBuffers(akit,input); if (!bufSize) { return; } liquid_float_complex x, y; for (size_t i = 0; i < bufSize; i++) { // Reject upper band nco_crcf_step(ssbShift); nco_crcf_mix_up(ssbShift, input->data[i], &x); iirfilt_crcf_execute(ssbFilt, x, &y); nco_crcf_mix_down(ssbShift, y, &x); firhilbf_c2r_execute(c2rFilt, x, &demodOutputData[i]); } buildAudioOutput(akit, audioOut, true); } CubicSDR-0.2.3/src/modules/modem/analog/ModemLSB.h000066400000000000000000000010271322677621400215000ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemAnalog.h" class ModemLSB : public ModemAnalog { public: ModemLSB(); ~ModemLSB(); std::string getName(); static ModemBase *factory(); int checkSampleRate(long long sampleRate, int audioSampleRate); int getDefaultSampleRate(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: iirfilt_crcf ssbFilt; firhilbf c2rFilt; nco_crcf ssbShift; };CubicSDR-0.2.3/src/modules/modem/analog/ModemNBFM.cpp000066400000000000000000000014521322677621400221370ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemNBFM.h" ModemNBFM::ModemNBFM() : ModemAnalog() { demodFM = freqdem_create(0.5); } ModemNBFM::~ModemNBFM() { freqdem_destroy(demodFM); } ModemBase *ModemNBFM::factory() { return new ModemNBFM; } std::string ModemNBFM::getName() { return "NBFM"; } int ModemNBFM::getDefaultSampleRate() { return 12500; } void ModemNBFM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitAnalog *fmkit = (ModemKitAnalog *)kit; initOutputBuffers(fmkit, input); if (!bufSize) { return; } freqdem_demodulate_block(demodFM, &input->data[0], (unsigned int)bufSize, &demodOutputData[0]); buildAudioOutput(fmkit, audioOut, false); } CubicSDR-0.2.3/src/modules/modem/analog/ModemNBFM.h000066400000000000000000000006621322677621400216060ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Modem.h" #include "ModemAnalog.h" class ModemNBFM : public ModemAnalog { public: ModemNBFM(); ~ModemNBFM(); std::string getName(); static ModemBase *factory(); int getDefaultSampleRate(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: freqdem demodFM; };CubicSDR-0.2.3/src/modules/modem/analog/ModemUSB.cpp000066400000000000000000000030731322677621400220470ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemUSB.h" ModemUSB::ModemUSB() : ModemAnalog() { // half band filter used for side-band elimination ssbFilt = iirfilt_crcf_create_lowpass(6, 0.25); ssbShift = nco_crcf_create(LIQUID_NCO); nco_crcf_set_frequency(ssbShift, (float)((2.0 * M_PI) * 0.25)); c2rFilt = firhilbf_create(5, 90.0); useSignalOutput(true); } ModemBase *ModemUSB::factory() { return new ModemUSB; } std::string ModemUSB::getName() { return "USB"; } ModemUSB::~ModemUSB() { iirfilt_crcf_destroy(ssbFilt); nco_crcf_destroy(ssbShift); firhilbf_destroy(c2rFilt); } int ModemUSB::checkSampleRate(long long sampleRate, int /* audioSampleRate */) { if (sampleRate < MIN_BANDWIDTH) { return MIN_BANDWIDTH; } if (sampleRate % 2 == 0) { return (int)sampleRate; } return (int)(sampleRate+1); } int ModemUSB::getDefaultSampleRate() { return 5400; } void ModemUSB::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitAnalog *akit = (ModemKitAnalog *)kit; initOutputBuffers(akit,input); if (!bufSize) { return; } liquid_float_complex x, y; for (size_t i = 0; i < bufSize; i++) { // Reject lower band nco_crcf_step(ssbShift); nco_crcf_mix_down(ssbShift, input->data[i], &x); iirfilt_crcf_execute(ssbFilt, x, &y); nco_crcf_mix_up(ssbShift, y, &x); firhilbf_c2r_execute(c2rFilt, x, &demodOutputData[i]); } buildAudioOutput(akit, audioOut, true); } CubicSDR-0.2.3/src/modules/modem/analog/ModemUSB.h000066400000000000000000000010241322677621400215060ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemAnalog.h" class ModemUSB : public ModemAnalog { public: ModemUSB(); ~ModemUSB(); std::string getName(); static ModemBase *factory(); int checkSampleRate(long long sampleRate, int audioSampleRate); int getDefaultSampleRate(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: iirfilt_crcf ssbFilt; firhilbf c2rFilt; nco_crcf ssbShift; };CubicSDR-0.2.3/src/modules/modem/digital/000077500000000000000000000000001322677621400201015ustar00rootroot00000000000000CubicSDR-0.2.3/src/modules/modem/digital/ModemAPSK.cpp000066400000000000000000000056271322677621400223370ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemAPSK.h" ModemAPSK::ModemAPSK() : ModemDigital() { demodAPSK4 = modem_create(LIQUID_MODEM_APSK4); demodAPSK8 = modem_create(LIQUID_MODEM_APSK8); demodAPSK16 = modem_create(LIQUID_MODEM_APSK16); demodAPSK32 = modem_create(LIQUID_MODEM_APSK32); demodAPSK64 = modem_create(LIQUID_MODEM_APSK64); demodAPSK128 = modem_create(LIQUID_MODEM_APSK128); demodAPSK256 = modem_create(LIQUID_MODEM_APSK256); demodAPSK = demodAPSK4; cons = 4; } ModemBase *ModemAPSK::factory() { return new ModemAPSK; } ModemAPSK::~ModemAPSK() { modem_destroy(demodAPSK4); modem_destroy(demodAPSK8); modem_destroy(demodAPSK16); modem_destroy(demodAPSK32); modem_destroy(demodAPSK64); modem_destroy(demodAPSK128); modem_destroy(demodAPSK256); } std::string ModemAPSK::getName() { return "APSK"; } ModemArgInfoList ModemAPSK::getSettings() { ModemArgInfoList args; ModemArgInfo consArg; consArg.key = "cons"; consArg.name = "Constellation"; consArg.description = "Modem Constellation Pattern"; consArg.value = std::to_string(cons); consArg.type = ModemArgInfo::STRING; std::vector consOpts; consOpts.push_back("4"); consOpts.push_back("8"); consOpts.push_back("16"); consOpts.push_back("32"); consOpts.push_back("64"); consOpts.push_back("128"); consOpts.push_back("256"); consArg.options = consOpts; args.push_back(consArg); return args; } void ModemAPSK::writeSetting(std::string setting, std::string value) { if (setting == "cons") { int newCons = std::stoi(value); updateDemodulatorCons(newCons); } } std::string ModemAPSK::readSetting(std::string setting) { if (setting == "cons") { return std::to_string(cons); } return ""; } void ModemAPSK::updateDemodulatorCons(int cons) { this->cons = cons; switch (cons) { case 4: demodAPSK = demodAPSK4; break; case 8: demodAPSK = demodAPSK8; break; case 16: demodAPSK = demodAPSK16; break; case 32: demodAPSK = demodAPSK32; break; case 64: demodAPSK = demodAPSK64; break; case 128: demodAPSK = demodAPSK128; break; case 256: demodAPSK = demodAPSK256; break; } } void ModemAPSK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodAPSK, input); for (size_t i = 0, bufSize = input->data.size(); i < bufSize; i++) { modem_demodulate(demodAPSK, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodAPSK, 0.005f); digitalFinish(dkit, demodAPSK); } CubicSDR-0.2.3/src/modules/modem/digital/ModemAPSK.h000066400000000000000000000013601322677621400217720ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemAPSK : public ModemDigital { public: ModemAPSK(); ~ModemAPSK(); std::string getName(); static ModemBase *factory(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); void updateDemodulatorCons(int cons); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: int cons; modem demodAPSK; modem demodAPSK4; modem demodAPSK8; modem demodAPSK16; modem demodAPSK32; modem demodAPSK64; modem demodAPSK128; modem demodAPSK256; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemASK.cpp000066400000000000000000000057531322677621400222170ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemASK.h" ModemASK::ModemASK() : ModemDigital() { demodASK2 = modem_create(LIQUID_MODEM_ASK2); demodASK4 = modem_create(LIQUID_MODEM_ASK4); demodASK8 = modem_create(LIQUID_MODEM_ASK8); demodASK16 = modem_create(LIQUID_MODEM_ASK16); demodASK32 = modem_create(LIQUID_MODEM_ASK32); demodASK64 = modem_create(LIQUID_MODEM_ASK64); demodASK128 = modem_create(LIQUID_MODEM_ASK128); demodASK256 = modem_create(LIQUID_MODEM_ASK256); demodASK = demodASK2; cons = 2; } ModemBase *ModemASK::factory() { return new ModemASK; } ModemASK::~ModemASK() { modem_destroy(demodASK4); modem_destroy(demodASK8); modem_destroy(demodASK16); modem_destroy(demodASK32); modem_destroy(demodASK64); modem_destroy(demodASK128); modem_destroy(demodASK256); } std::string ModemASK::getName() { return "ASK"; } ModemArgInfoList ModemASK::getSettings() { ModemArgInfoList args; ModemArgInfo consArg; consArg.key = "cons"; consArg.name = "Constellation"; consArg.description = "Modem Constellation Pattern"; consArg.value = std::to_string(cons); consArg.type = ModemArgInfo::STRING; std::vector consOpts; consOpts.push_back("2"); consOpts.push_back("4"); consOpts.push_back("8"); consOpts.push_back("16"); consOpts.push_back("32"); consOpts.push_back("64"); consOpts.push_back("128"); consOpts.push_back("256"); consArg.options = consOpts; args.push_back(consArg); return args; } void ModemASK::writeSetting(std::string setting, std::string value) { if (setting == "cons") { int newCons = std::stoi(value); updateDemodulatorCons(newCons); } } std::string ModemASK::readSetting(std::string setting) { if (setting == "cons") { return std::to_string(cons); } return ""; } void ModemASK::updateDemodulatorCons(int cons) { this->cons = cons; switch (cons) { case 2: demodASK = demodASK2; break; case 4: demodASK = demodASK4; break; case 8: demodASK = demodASK8; break; case 16: demodASK = demodASK16; break; case 32: demodASK = demodASK32; break; case 64: demodASK = demodASK64; break; case 128: demodASK = demodASK128; break; case 256: demodASK = demodASK256; break; } } void ModemASK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodASK, input); for (size_t i = 0, bufSize = input->data.size(); i < bufSize; i++) { modem_demodulate(demodASK, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodASK, 0.005f); digitalFinish(dkit, demodASK); } CubicSDR-0.2.3/src/modules/modem/digital/ModemASK.h000066400000000000000000000014061322677621400216530ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemASK : public ModemDigital { public: ModemASK(); ~ModemASK(); std::string getName(); static ModemBase *factory(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); void updateDemodulatorCons(int cons); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: int cons; modem demodASK; modem demodASK2; modem demodASK4; modem demodASK8; modem demodASK16; modem demodASK32; modem demodASK64; modem demodASK128; modem demodASK256; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemBPSK.cpp000066400000000000000000000014451322677621400223320ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemBPSK.h" ModemBPSK::ModemBPSK() : ModemDigital() { demodBPSK = modem_create(LIQUID_MODEM_BPSK); } ModemBase *ModemBPSK::factory() { return new ModemBPSK; } ModemBPSK::~ModemBPSK() { modem_destroy(demodBPSK); } std::string ModemBPSK::getName() { return "BPSK"; } void ModemBPSK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodBPSK, input); for (size_t i = 0, bufSize=input->data.size(); i < bufSize; i++) { modem_demodulate(demodBPSK, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodBPSK, 0.005f); digitalFinish(dkit, demodBPSK); } CubicSDR-0.2.3/src/modules/modem/digital/ModemBPSK.h000066400000000000000000000006051322677621400217740ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemBPSK : public ModemDigital { public: ModemBPSK(); ~ModemBPSK(); std::string getName(); static ModemBase *factory(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: modem demodBPSK; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemDPSK.cpp000066400000000000000000000061041322677621400223310ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemDPSK.h" ModemDPSK::ModemDPSK() : ModemDigital() { demodDPSK2 = modem_create(LIQUID_MODEM_DPSK2); demodDPSK4 = modem_create(LIQUID_MODEM_DPSK4); demodDPSK8 = modem_create(LIQUID_MODEM_DPSK8); demodDPSK16 = modem_create(LIQUID_MODEM_DPSK16); demodDPSK32 = modem_create(LIQUID_MODEM_DPSK32); demodDPSK64 = modem_create(LIQUID_MODEM_DPSK64); demodDPSK128 = modem_create(LIQUID_MODEM_DPSK128); demodDPSK256 = modem_create(LIQUID_MODEM_DPSK256); demodDPSK = demodDPSK2; cons = 2; } ModemBase *ModemDPSK::factory() { return new ModemDPSK; } std::string ModemDPSK::getName() { return "DPSK"; } ModemDPSK::~ModemDPSK() { modem_destroy(demodDPSK2); modem_destroy(demodDPSK4); modem_destroy(demodDPSK8); modem_destroy(demodDPSK16); modem_destroy(demodDPSK32); modem_destroy(demodDPSK64); modem_destroy(demodDPSK128); modem_destroy(demodDPSK256); } ModemArgInfoList ModemDPSK::getSettings() { ModemArgInfoList args; ModemArgInfo consArg; consArg.key = "cons"; consArg.name = "Constellation"; consArg.description = "Modem Constellation Pattern"; consArg.value = std::to_string(cons); consArg.type = ModemArgInfo::STRING; std::vector consOpts; consOpts.push_back("2"); consOpts.push_back("4"); consOpts.push_back("8"); consOpts.push_back("16"); consOpts.push_back("32"); consOpts.push_back("64"); consOpts.push_back("128"); consOpts.push_back("256"); consArg.options = consOpts; args.push_back(consArg); return args; } void ModemDPSK::writeSetting(std::string setting, std::string value) { if (setting == "cons") { int newCons = std::stoi(value); updateDemodulatorCons(newCons); } } std::string ModemDPSK::readSetting(std::string setting) { if (setting == "cons") { return std::to_string(cons); } return ""; } void ModemDPSK::updateDemodulatorCons(int cons) { this->cons = cons; switch (cons) { case 2: demodDPSK = demodDPSK2; break; case 4: demodDPSK = demodDPSK4; break; case 8: demodDPSK = demodDPSK8; break; case 16: demodDPSK = demodDPSK16; break; case 32: demodDPSK = demodDPSK32; break; case 64: demodDPSK = demodDPSK64; break; case 128: demodDPSK = demodDPSK128; break; case 256: demodDPSK = demodDPSK256; break; } } void ModemDPSK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodDPSK, input); for (size_t i = 0, bufSize = input->data.size(); i < bufSize; i++) { modem_demodulate(demodDPSK, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodDPSK, 0.005f); digitalFinish(dkit, demodDPSK); } CubicSDR-0.2.3/src/modules/modem/digital/ModemDPSK.h000066400000000000000000000014171322677621400220000ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemDPSK : public ModemDigital { public: ModemDPSK(); ~ModemDPSK(); std::string getName(); static ModemBase *factory(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); void updateDemodulatorCons(int cons); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: int cons; modem demodDPSK; modem demodDPSK2; modem demodDPSK4; modem demodDPSK8; modem demodDPSK16; modem demodDPSK32; modem demodDPSK64; modem demodDPSK128; modem demodDPSK256; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemFSK.cpp000066400000000000000000000072131322677621400222150ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemFSK.h" #include ModemFSK::ModemFSK() : ModemDigital() { // DMR defaults? bps = 1; sps = 9600; bw = 0.45f; outStream << std::hex; } ModemBase *ModemFSK::factory() { return new ModemFSK; } int ModemFSK::checkSampleRate(long long sampleRate, int audioSampleRate) { double minSps = pow(2.0,bps); double nextSps = (double(sampleRate) / double(sps)); if (nextSps < minSps) { return 2 * bps * sps; } else { return (int)sampleRate; } } int ModemFSK::getDefaultSampleRate() { return 19200; } ModemArgInfoList ModemFSK::getSettings() { ModemArgInfoList args; ModemArgInfo bpsArg; bpsArg.key = "bps"; bpsArg.name = "Bits/symbol"; bpsArg.value = std::to_string(bps); bpsArg.description = "Modem bits-per-symbol"; bpsArg.type = ModemArgInfo::STRING; bpsArg.units = "bits"; std::vector bpsOpts; bpsOpts.push_back("1"); bpsOpts.push_back("2"); bpsOpts.push_back("4"); bpsOpts.push_back("8"); bpsOpts.push_back("16"); bpsArg.options = bpsOpts; args.push_back(bpsArg); ModemArgInfo spsArg; spsArg.key = "sps"; spsArg.name = "Symbols/second"; spsArg.value = std::to_string(sps); spsArg.description = "Modem symbols-per-second"; spsArg.type = ModemArgInfo::INT; spsArg.range = ModemRange(10,115200); std::vector spsOpts; args.push_back(spsArg); ModemArgInfo bwArg; bwArg.key = "bw"; bwArg.name = "Signal bandwidth"; bwArg.value = std::to_string(bw); bwArg.description = "Total signal bandwidth"; bwArg.type = ModemArgInfo::FLOAT; bwArg.range = ModemRange(0.1,0.49); args.push_back(bwArg); return args; } void ModemFSK::writeSetting(std::string setting, std::string value) { if (setting == "bps") { bps = std::stoi(value); rebuildKit(); } else if (setting == "sps") { sps = std::stoi(value); rebuildKit(); } else if (setting == "bw") { bw = std::stof(value); rebuildKit(); } } std::string ModemFSK::readSetting(std::string setting) { if (setting == "bps") { return std::to_string(bps); } else if (setting == "sps") { return std::to_string(sps); } else if (setting == "bw") { return std::to_string(bw); } return ""; } ModemKit *ModemFSK::buildKit(long long sampleRate, int audioSampleRate) { ModemKitFSK *dkit = new ModemKitFSK; dkit->m = bps; dkit->k = (unsigned int)(sampleRate / sps); dkit->bw = bw; dkit->demodFSK = fskdem_create(dkit->m, dkit->k, dkit->bw); dkit->sampleRate = sampleRate; dkit->audioSampleRate = audioSampleRate; return dkit; } void ModemFSK::disposeKit(ModemKit *kit) { ModemKitFSK *dkit = (ModemKitFSK *)kit; fskdem_destroy(dkit->demodFSK); delete dkit; } std::string ModemFSK::getName() { return "FSK"; } ModemFSK::~ModemFSK() { } void ModemFSK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitFSK *dkit = (ModemKitFSK *)kit; digitalStart(dkit, nullptr, input); dkit->inputBuffer.insert(dkit->inputBuffer.end(),input->data.begin(),input->data.end()); while (dkit->inputBuffer.size() >= dkit->k) { outStream << fskdem_demodulate(dkit->demodFSK, &dkit->inputBuffer[0]); // float err = fskdem_get_frequency_error(dkit->demodFSK); dkit->inputBuffer.erase(dkit->inputBuffer.begin(),dkit->inputBuffer.begin()+dkit->k); } digitalFinish(dkit, nullptr); }CubicSDR-0.2.3/src/modules/modem/digital/ModemFSK.h000066400000000000000000000016641322677621400216660ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" #include class ModemKitFSK : public ModemKitDigital { public: unsigned int m, k; float bw; fskdem demodFSK; std::vector inputBuffer; }; class ModemFSK : public ModemDigital { public: ModemFSK(); ~ModemFSK(); std::string getName(); static ModemBase *factory(); int checkSampleRate(long long sampleRate, int audioSampleRate); int getDefaultSampleRate(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); ModemKit *buildKit(long long sampleRate, int audioSampleRate); void disposeKit(ModemKit *kit); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: int sps, bps; float bw; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemGMSK.cpp000066400000000000000000000067751322677621400223470ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemGMSK.h" #include ModemGMSK::ModemGMSK() : ModemDigital() { _sps = 4; _fdelay = 3; _ebf = 0.3f; outStream << std::hex; } ModemGMSK::~ModemGMSK() { } std::string ModemGMSK::getName() { return "GMSK"; } ModemBase *ModemGMSK::factory() { return new ModemGMSK; } int ModemGMSK::checkSampleRate(long long sampleRate, int audioSampleRate) { if (sampleRate < MIN_BANDWIDTH) { return MIN_BANDWIDTH; } return (int)sampleRate; } int ModemGMSK::getDefaultSampleRate() { return 19200; } ModemArgInfoList ModemGMSK::getSettings() { ModemArgInfoList args; ModemArgInfo fdelayArg; fdelayArg.key = "fdelay"; fdelayArg.name = "Filter delay"; fdelayArg.value = std::to_string(_fdelay); fdelayArg.description = "Filter delay in samples"; fdelayArg.type = ModemArgInfo::INT; fdelayArg.units = "samples"; fdelayArg.range = ModemRange(1,128); args.push_back(fdelayArg); ModemArgInfo spsArg; spsArg.key = "sps"; spsArg.name = "Samples / symbol"; spsArg.value = std::to_string(_sps); spsArg.description = "Modem samples-per-symbol"; spsArg.type = ModemArgInfo::INT; spsArg.units = "samples/symbol"; spsArg.range = ModemRange(2,512); args.push_back(spsArg); ModemArgInfo ebfArg; ebfArg.key = "ebf"; ebfArg.name = "Excess bandwidth"; ebfArg.value = std::to_string(_ebf); ebfArg.description = "Modem excess bandwidth factor"; ebfArg.type = ModemArgInfo::FLOAT; ebfArg.range = ModemRange(0.1,0.49); args.push_back(ebfArg); return args; } void ModemGMSK::writeSetting(std::string setting, std::string value) { if (setting == "fdelay") { _fdelay = std::stoi(value); rebuildKit(); } else if (setting == "sps") { _sps = std::stoi(value); rebuildKit(); } else if (setting == "ebf") { _ebf = std::stof(value); rebuildKit(); } } std::string ModemGMSK::readSetting(std::string setting) { if (setting == "fdelay") { return std::to_string(_fdelay); } else if (setting == "sps") { return std::to_string(_sps); } else if (setting == "ebf") { return std::to_string(_ebf); } return ""; } ModemKit *ModemGMSK::buildKit(long long sampleRate, int audioSampleRate) { ModemKitGMSK *dkit = new ModemKitGMSK; dkit->sps = _sps; dkit->fdelay = _fdelay; dkit->ebf = _ebf; dkit->demodGMSK = gmskdem_create(dkit->sps, dkit->fdelay, dkit->ebf); dkit->sampleRate = sampleRate; dkit->audioSampleRate = audioSampleRate; return dkit; } void ModemGMSK::disposeKit(ModemKit *kit) { ModemKitGMSK *dkit = (ModemKitGMSK *)kit; gmskdem_destroy(dkit->demodGMSK); delete dkit; } void ModemGMSK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitGMSK *dkit = (ModemKitGMSK *)kit; unsigned int sym_out; digitalStart(dkit, nullptr, input); dkit->inputBuffer.insert(dkit->inputBuffer.end(),input->data.begin(),input->data.end()); int numProcessed = 0; for (size_t i = 0, iMax = dkit->inputBuffer.size()/dkit->sps; i < iMax; i+= dkit->sps) { gmskdem_demodulate(dkit->demodGMSK, &input->data[i],&sym_out); outStream << sym_out; numProcessed += dkit->sps; } dkit->inputBuffer.erase(dkit->inputBuffer.begin(),dkit->inputBuffer.begin()+numProcessed); digitalFinish(dkit, nullptr); } CubicSDR-0.2.3/src/modules/modem/digital/ModemGMSK.h000066400000000000000000000017751322677621400220070ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" #include class ModemKitGMSK : public ModemKitDigital { public: unsigned int fdelay, sps; float ebf; gmskdem demodGMSK; std::vector inputBuffer; }; class ModemGMSK : public ModemDigital { public: ModemGMSK(); ~ModemGMSK(); std::string getName(); static ModemBase *factory(); int checkSampleRate(long long sampleRate, int audioSampleRate); int getDefaultSampleRate(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); ModemKit *buildKit(long long sampleRate, int audioSampleRate); void disposeKit(ModemKit *kit); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: int _sps; // samples per symbol int _fdelay; // filter delay float _ebf; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemOOK.cpp000066400000000000000000000016671322677621400222310ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemOOK.h" ModemOOK::ModemOOK() : ModemDigital() { demodOOK = modem_create(LIQUID_MODEM_OOK); } ModemOOK::~ModemOOK() { modem_destroy(demodOOK); } std::string ModemOOK::getName() { return "OOK"; } ModemBase *ModemOOK::factory() { return new ModemOOK; } int ModemOOK::checkSampleRate(long long sampleRate, int audioSampleRate) { if (sampleRate < 100) { return 100; } return (int)sampleRate; } void ModemOOK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodOOK, input); for (size_t i = 0, bufSize=input->data.size(); i < bufSize; i++) { modem_demodulate(demodOOK, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodOOK, 0.005f); digitalFinish(dkit, demodOOK); } CubicSDR-0.2.3/src/modules/modem/digital/ModemOOK.h000066400000000000000000000007121322677621400216640ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemOOK : public ModemDigital { public: ModemOOK(); ~ModemOOK(); std::string getName(); static ModemBase *factory(); int checkSampleRate(long long sampleRate, int audioSampleRate); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: modem demodOOK; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemPSK.cpp000066400000000000000000000060121322677621400222230ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemPSK.h" ModemPSK::ModemPSK() : ModemDigital() { demodPSK2 = modem_create(LIQUID_MODEM_PSK2); demodPSK4 = modem_create(LIQUID_MODEM_PSK4); demodPSK8 = modem_create(LIQUID_MODEM_PSK8); demodPSK16 = modem_create(LIQUID_MODEM_PSK16); demodPSK32 = modem_create(LIQUID_MODEM_PSK32); demodPSK64 = modem_create(LIQUID_MODEM_PSK64); demodPSK128 = modem_create(LIQUID_MODEM_PSK128); demodPSK256 = modem_create(LIQUID_MODEM_PSK256); demodPSK = demodPSK2; cons = 2; } ModemBase *ModemPSK::factory() { return new ModemPSK; } std::string ModemPSK::getName() { return "PSK"; } ModemPSK::~ModemPSK() { modem_destroy(demodPSK2); modem_destroy(demodPSK4); modem_destroy(demodPSK8); modem_destroy(demodPSK16); modem_destroy(demodPSK32); modem_destroy(demodPSK64); modem_destroy(demodPSK128); modem_destroy(demodPSK256); } ModemArgInfoList ModemPSK::getSettings() { ModemArgInfoList args; ModemArgInfo consArg; consArg.key = "cons"; consArg.name = "Constellation"; consArg.description = "Modem Constellation Pattern"; consArg.value = std::to_string(cons); consArg.type = ModemArgInfo::STRING; std::vector consOpts; consOpts.push_back("2"); consOpts.push_back("4"); consOpts.push_back("8"); consOpts.push_back("16"); consOpts.push_back("32"); consOpts.push_back("64"); consOpts.push_back("128"); consOpts.push_back("256"); consArg.options = consOpts; args.push_back(consArg); return args; } void ModemPSK::writeSetting(std::string setting, std::string value) { if (setting == "cons") { int newCons = std::stoi(value); updateDemodulatorCons(newCons); } } std::string ModemPSK::readSetting(std::string setting) { if (setting == "cons") { return std::to_string(cons); } return ""; } void ModemPSK::updateDemodulatorCons(int cons) { this->cons = cons; switch (cons) { case 2: demodPSK = demodPSK2; break; case 4: demodPSK = demodPSK4; break; case 8: demodPSK = demodPSK8; break; case 16: demodPSK = demodPSK16; break; case 32: demodPSK = demodPSK32; break; case 64: demodPSK = demodPSK64; break; case 128: demodPSK = demodPSK128; break; case 256: demodPSK = demodPSK256; break; } } void ModemPSK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodPSK, input); for (size_t i = 0, bufSize = input->data.size(); i < bufSize; i++) { modem_demodulate(demodPSK, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodPSK, 0.005f); digitalFinish(dkit, demodPSK); } CubicSDR-0.2.3/src/modules/modem/digital/ModemPSK.h000066400000000000000000000014071322677621400216730ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemPSK : public ModemDigital { public: ModemPSK(); ~ModemPSK(); std::string getName(); static ModemBase *factory(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); void updateDemodulatorCons(int cons); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: int cons; modem demodPSK; modem demodPSK2; modem demodPSK4; modem demodPSK8; modem demodPSK16; modem demodPSK32; modem demodPSK64; modem demodPSK128; modem demodPSK256; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemQAM.cpp000066400000000000000000000055241322677621400222130ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemQAM.h" ModemQAM::ModemQAM() : ModemDigital() { demodQAM4 = modem_create(LIQUID_MODEM_QAM4); demodQAM8 = modem_create(LIQUID_MODEM_QAM8); demodQAM16 = modem_create(LIQUID_MODEM_QAM16); demodQAM32 = modem_create(LIQUID_MODEM_QAM32); demodQAM64 = modem_create(LIQUID_MODEM_QAM64); demodQAM128 = modem_create(LIQUID_MODEM_QAM128); demodQAM256 = modem_create(LIQUID_MODEM_QAM256); demodQAM = demodQAM4; cons = 4; } ModemBase *ModemQAM::factory() { return new ModemQAM; } std::string ModemQAM::getName() { return "QAM"; } ModemQAM::~ModemQAM() { modem_destroy(demodQAM4); modem_destroy(demodQAM8); modem_destroy(demodQAM16); modem_destroy(demodQAM32); modem_destroy(demodQAM64); modem_destroy(demodQAM128); modem_destroy(demodQAM256); } ModemArgInfoList ModemQAM::getSettings() { ModemArgInfoList args; ModemArgInfo consArg; consArg.key = "cons"; consArg.name = "Constellation"; consArg.description = "Modem Constellation Pattern"; consArg.value = std::to_string(cons); consArg.type = ModemArgInfo::STRING; std::vector consOpts; consOpts.push_back("4"); consOpts.push_back("8"); consOpts.push_back("16"); consOpts.push_back("32"); consOpts.push_back("64"); consOpts.push_back("128"); consOpts.push_back("256"); consArg.options = consOpts; args.push_back(consArg); return args; } void ModemQAM::writeSetting(std::string setting, std::string value) { if (setting == "cons") { int newCons = std::stoi(value); updateDemodulatorCons(newCons); } } std::string ModemQAM::readSetting(std::string setting) { if (setting == "cons") { return std::to_string(cons); } return ""; } void ModemQAM::updateDemodulatorCons(int cons) { this->cons = cons; switch (cons) { case 4: demodQAM = demodQAM4; break; case 8: demodQAM = demodQAM8; break; case 16: demodQAM = demodQAM16; break; case 32: demodQAM = demodQAM32; break; case 64: demodQAM = demodQAM64; break; case 128: demodQAM = demodQAM128; break; case 256: demodQAM = demodQAM256; break; } } void ModemQAM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodQAM, input); for (size_t i = 0, bufSize = input->data.size(); i < bufSize; i++) { modem_demodulate(demodQAM, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodQAM, 0.5f); digitalFinish(dkit, demodQAM); } CubicSDR-0.2.3/src/modules/modem/digital/ModemQAM.h000066400000000000000000000013631322677621400216550ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemQAM : public ModemDigital { public: ModemQAM(); ~ModemQAM(); std::string getName(); static ModemBase *factory(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); void updateDemodulatorCons(int cons); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: int cons; modem demodQAM; modem demodQAM4; modem demodQAM8; modem demodQAM16; modem demodQAM32; modem demodQAM64; modem demodQAM128; modem demodQAM256; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemQPSK.cpp000066400000000000000000000014451322677621400223510ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemQPSK.h" ModemQPSK::ModemQPSK() : ModemDigital() { demodQPSK = modem_create(LIQUID_MODEM_QPSK); } ModemBase *ModemQPSK::factory() { return new ModemQPSK; } ModemQPSK::~ModemQPSK() { modem_destroy(demodQPSK); } std::string ModemQPSK::getName() { return "QPSK"; } void ModemQPSK::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodQPSK, input); for (size_t i = 0, bufSize = input->data.size(); i < bufSize; i++) { modem_demodulate(demodQPSK, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodQPSK, 0.8f); digitalFinish(dkit, demodQPSK); } CubicSDR-0.2.3/src/modules/modem/digital/ModemQPSK.h000066400000000000000000000006051322677621400220130ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemQPSK : public ModemDigital { public: ModemQPSK(); ~ModemQPSK(); std::string getName(); static ModemBase *factory(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: modem demodQPSK; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemSQAM.cpp000066400000000000000000000037531322677621400223400ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemSQAM.h" ModemSQAM::ModemSQAM() : ModemDigital() { demodSQAM32 = modem_create(LIQUID_MODEM_SQAM32); demodSQAM128 = modem_create(LIQUID_MODEM_SQAM128); demodSQAM = demodSQAM32; cons = 32; } ModemBase *ModemSQAM::factory() { return new ModemSQAM; } ModemSQAM::~ModemSQAM() { modem_destroy(demodSQAM32); modem_destroy(demodSQAM128); } std::string ModemSQAM::getName() { return "SQAM"; } ModemArgInfoList ModemSQAM::getSettings() { ModemArgInfoList args; ModemArgInfo consArg; consArg.key = "cons"; consArg.name = "Constellation"; consArg.description = "Modem Constellation Pattern"; consArg.value = std::to_string(cons); consArg.type = ModemArgInfo::STRING; std::vector consOpts; consOpts.push_back("32"); consOpts.push_back("128"); consArg.options = consOpts; args.push_back(consArg); return args; } void ModemSQAM::writeSetting(std::string setting, std::string value) { if (setting == "cons") { int newCons = std::stoi(value); updateDemodulatorCons(newCons); } } std::string ModemSQAM::readSetting(std::string setting) { if (setting == "cons") { return std::to_string(cons); } return ""; } void ModemSQAM::updateDemodulatorCons(int cons) { this->cons = cons; switch (cons) { case 32: demodSQAM = demodSQAM32; break; case 128: demodSQAM = demodSQAM128; break; } } void ModemSQAM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodSQAM, input); for (size_t i = 0, bufSize = input->data.size(); i < bufSize; i++) { modem_demodulate(demodSQAM, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodSQAM, 0.005f); digitalFinish(dkit, demodSQAM); } CubicSDR-0.2.3/src/modules/modem/digital/ModemSQAM.h000066400000000000000000000012061322677621400217740ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemSQAM : public ModemDigital { public: ModemSQAM(); ~ModemSQAM(); std::string getName(); static ModemBase *factory(); ModemArgInfoList getSettings(); void writeSetting(std::string setting, std::string value); std::string readSetting(std::string setting); void updateDemodulatorCons(int cons); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: int cons; modem demodSQAM; modem demodSQAM32; modem demodSQAM128; }; CubicSDR-0.2.3/src/modules/modem/digital/ModemST.cpp000066400000000000000000000014061322677621400221160ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModemST.h" ModemST::ModemST() : ModemDigital() { demodST = modem_create(LIQUID_MODEM_V29); } ModemBase *ModemST::factory() { return new ModemST; } std::string ModemST::getName() { return "ST"; } ModemST::~ModemST() { modem_destroy(demodST); } void ModemST::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) { ModemKitDigital *dkit = (ModemKitDigital *)kit; digitalStart(dkit, demodST, input); for (size_t i = 0, bufSize = input->data.size(); i < bufSize; i++) { modem_demodulate(demodST, input->data[i], &demodOutputDataDigital[i]); } updateDemodulatorLock(demodST, 0.005f); digitalFinish(dkit, demodST); } CubicSDR-0.2.3/src/modules/modem/digital/ModemST.h000066400000000000000000000006021322677621400215600ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "ModemDigital.h" class ModemST : public ModemDigital { public: ModemST(); ~ModemST(); std::string getName(); static ModemBase *factory(); void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut); private: modem demodST; }; CubicSDR-0.2.3/src/panel/000077500000000000000000000000001322677621400150125ustar00rootroot00000000000000CubicSDR-0.2.3/src/panel/MeterPanel.cpp000066400000000000000000000120501322677621400175500ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "MeterPanel.h" #include "ColorTheme.h" MeterPanel::MeterPanel(std::string name, float low, float high, float current) { this->name = name; this->low = low; this->high = high; this->current = current; RGBA4f c1, c2; setFill(GLPanel::GLPANEL_FILL_NONE); bgPanel.setBorderPx(1); bgPanel.setCoordinateSystem(GLPanel::GLPANEL_Y_UP); bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_X); levelPanel.setBorderPx(0); levelPanel.setMarginPx(1); setPanelLevel(current, levelPanel); levelPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); levelPanel.setBlend(GL_ONE, GL_ONE); bgPanel.addChild(&levelPanel); setPanelLevel(current, highlightPanel); highlightPanel.setBorderPx(0); highlightPanel.setMarginPx(1); highlightPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); highlightPanel.setBlend(GL_ONE, GL_ONE); highlightPanel.visible = false; c1 = RGBA4f(0.3f,0.3f,0.3f,1.0f); c2 = RGBA4f(0.65f,0.65f,0.65f,1.0f);; highlightPanel.setFillColor(c1, c2); bgPanel.addChild(&highlightPanel); addChild(&bgPanel); labelPanel.setSize(1.0f, 0.1f); labelPanel.setPosition(0.0, 1.0); labelPanel.setText(name,GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, true); labelPanel.setFill(GLPanel::GLPANEL_FILL_NONE); addChild(&labelPanel); valuePanel.setSize(1.0f, 0.1f); valuePanel.setPosition(0.0, -1.0); setValueLabel(std::to_string(int(current))); valuePanel.setFill(GLPanel::GLPANEL_FILL_NONE); addChild(&valuePanel); } MeterPanel::~MeterPanel() { } void MeterPanel::setName(std::string name_in) { name = name_in; } std::string MeterPanel::getName() { return name; } void MeterPanel::setRange(float low, float high) { this->low = low; this->high = high; } float MeterPanel::getLow() { return this->low; } float MeterPanel::getHigh() { return this->high; } void MeterPanel::setValue(float value) { if (value > high) { value = high; } if (value < low) { value = low; } current = value; setValueLabel(std::to_string(int(current))); setPanelLevel(value, levelPanel); } void MeterPanel::setHighlight(float value) { if (value > high) { value = high; } if (value < low) { value = low; } setPanelLevel(value, highlightPanel); } void MeterPanel::setHighlightVisible(bool vis) { highlightPanel.visible = vis; } float MeterPanel::getValue() { return current; } bool MeterPanel::isMeterHit(CubicVR::vec2 mousePoint) { CubicVR::vec2 hitResult; if (bgPanel.hitTest(mousePoint, hitResult)) { return true; } return false; } float MeterPanel::getMeterHitValue(CubicVR::vec2 mousePoint) { CubicVR::vec2 hitResult; if (bgPanel.hitTest(mousePoint, hitResult)) { float hitLevel = ((hitResult.y + 1.0) * 0.5); if (hitLevel < 0.0f) { hitLevel = 0.0f; } if (hitLevel > 1.0f) { hitLevel = 1.0f; } return low + (hitLevel * (high-low)); } else { return 0; } } void MeterPanel::drawPanelContents() { GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); double viewHeight = vp[3]; CubicVR::vec4 t = CubicVR::mat4::vec4_multiply(CubicVR::vec4(0,0.5,0,1), transform); CubicVR::vec4 b = CubicVR::mat4::vec4_multiply(CubicVR::vec4(0,-0.5,0,1), transform); double hScale = t.y-b.y; viewHeight = viewHeight * hScale; double labelHeight = GLFont::getScaledPx(18, GLFont::getScaleFactor()); double labelPad = 8.0; double pScale = (1.0/viewHeight); RGBA4f c1, c2; bgPanel.setSize(1.0f, 1.0 - pScale * (labelHeight + labelPad * 2.0)); valuePanel.setPosition(0.0, (pScale * (labelHeight / 2.0 + labelPad) ) - 1.0); valuePanel.setSize(1.0, pScale*labelHeight); labelPanel.setPosition(0.0, 1.0 - (pScale * (labelHeight / 2.0 + labelPad))); labelPanel.setSize(1.0, pScale*labelHeight); c1 = ThemeMgr::mgr.currentTheme->generalBackground; c2 = ThemeMgr::mgr.currentTheme->generalBackground * 0.5; c1.a = 1.0; c2.a = 1.0; bgPanel.setFillColor(c1, c2); c1 = ThemeMgr::mgr.currentTheme->meterLevel * 0.5; c2 = ThemeMgr::mgr.currentTheme->meterLevel; c1.a = 1.0; c2.a = 1.0; levelPanel.setFillColor(c1, c2); drawChildren(); } void MeterPanel::setValueLabel(std::string label) { valuePanel.setText(label, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, true); } void MeterPanel::setPanelLevel(float setValue, GLPanel &panel) { float valueNorm = (setValue - low) / (high - low); panel.setSize(1.0, valueNorm); panel.setPosition(0.0, (-1.0+(valueNorm))); } bool MeterPanel::getChanged() { return changed; } void MeterPanel::setChanged(bool changed) { this->changed = changed; } CubicSDR-0.2.3/src/panel/MeterPanel.h000066400000000000000000000020371322677621400172210ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "GLPanel.h" class MeterPanel : public GLPanel { public: MeterPanel(std::string name, float low, float high, float current); ~MeterPanel(); void setName(std::string name_in); std::string getName(); void setRange(float low, float high); float getLow(); float getHigh(); void setValue(float value); void setHighlight(float value); void setHighlightVisible(bool vis); float getValue(); bool isMeterHit(CubicVR::vec2 mousePoint); float getMeterHitValue(CubicVR::vec2 mousePoint); void setChanged(bool changed); bool getChanged(); protected: void drawPanelContents(); void setValueLabel(std::string label); void setPanelLevel(float setValue, GLPanel &panel); private: std::string name; float low, high, current; bool changed = false; GLPanel bgPanel; GLPanel levelPanel; GLPanel highlightPanel; GLTextPanel labelPanel; GLTextPanel valuePanel; };CubicSDR-0.2.3/src/panel/ScopePanel.cpp000066400000000000000000000111021322677621400175420ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ScopePanel.h" #include "ColorTheme.h" ScopePanel::ScopePanel() : GLPanel(), scopeMode(SCOPE_MODE_Y) { setFill(GLPanelFillType::GLPANEL_FILL_NONE); bgPanel.setFill(GLPanelFillType::GLPANEL_FILL_GRAD_BAR_Y); bgPanelStereo[0].setFill(GLPanelFillType::GLPANEL_FILL_GRAD_BAR_Y); bgPanelStereo[0].setPosition(0, 0.5); bgPanelStereo[0].setSize(1, 0.5); bgPanelStereo[1].setFill(GLPanelFillType::GLPANEL_FILL_GRAD_BAR_Y); bgPanelStereo[1].setPosition(0, -0.5); bgPanelStereo[1].setSize(1, 0.5); } void ScopePanel::setMode(ScopeMode scopeMode) { this->scopeMode = scopeMode; } ScopePanel::ScopeMode ScopePanel::getMode() { return this->scopeMode; } void ScopePanel::setPoints(std::vector &points) { this->points.assign(points.begin(),points.end()); } void ScopePanel::drawPanelContents() { if (scopeMode == SCOPE_MODE_Y) { bgPanel.setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground, ThemeMgr::mgr.currentTheme->scopeBackground * 2.0); bgPanel.calcTransform(transform); bgPanel.draw(); glLineWidth(1.0); glEnable(GL_LINE_SMOOTH); glLoadMatrixf(transform.to_ptr()); glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r * 0.35, ThemeMgr::mgr.currentTheme->scopeLine.g * 0.35, ThemeMgr::mgr.currentTheme->scopeLine.b * 0.35); glBegin (GL_LINES); glVertex2f(-1.0, 0.0); glVertex2f(1.0, 0.0); glEnd(); } else if (scopeMode == SCOPE_MODE_2Y) { bgPanelStereo[0].setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground, ThemeMgr::mgr.currentTheme->scopeBackground * 2.0); bgPanelStereo[1].setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground, ThemeMgr::mgr.currentTheme->scopeBackground * 2.0); bgPanelStereo[0].calcTransform(transform); bgPanelStereo[0].draw(); bgPanelStereo[1].calcTransform(transform); bgPanelStereo[1].draw(); glLineWidth(1.0); glLoadMatrixf(transform.to_ptr()); glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r, ThemeMgr::mgr.currentTheme->scopeLine.g, ThemeMgr::mgr.currentTheme->scopeLine.b); glEnable(GL_LINE_SMOOTH); glBegin (GL_LINES); glVertex2f(-1.0, 0.0); glVertex2f(1.0, 0.0); glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r * 0.35, ThemeMgr::mgr.currentTheme->scopeLine.g * 0.35, ThemeMgr::mgr.currentTheme->scopeLine.b * 0.35); glVertex2f(-1.0, 0.5); glVertex2f(1.0, 0.5); glVertex2f(-1.0, -0.5); glVertex2f(1.0, -0.5); glEnd(); } else if (scopeMode == SCOPE_MODE_XY) { RGBA4f bg1(ThemeMgr::mgr.currentTheme->scopeBackground), bg2(ThemeMgr::mgr.currentTheme->scopeBackground * 2.0); bg1.a = 0.05f; bg2.a = 0.05f; bgPanel.setFillColor(bg1, bg2); bgPanel.calcTransform(transform); bgPanel.draw(); glLineWidth(1.0); glEnable(GL_POINT_SMOOTH); glPointSize(1.0); glLoadMatrixf(transform.to_ptr()); glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r * 0.15, ThemeMgr::mgr.currentTheme->scopeLine.g * 0.15, ThemeMgr::mgr.currentTheme->scopeLine.b * 0.15); } if (points.size()) { glEnable (GL_BLEND); glEnable (GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); if (scopeMode == SCOPE_MODE_XY) { glBlendFunc(GL_ONE, GL_ONE); } else { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } glColor4f(ThemeMgr::mgr.currentTheme->scopeLine.r, ThemeMgr::mgr.currentTheme->scopeLine.g, ThemeMgr::mgr.currentTheme->scopeLine.b, 1.0); glEnableClientState (GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &points[0]); glLineWidth(1.5); if (scopeMode == SCOPE_MODE_Y) { glLoadMatrixf(bgPanel.transform.to_ptr()); glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); } else if (scopeMode == SCOPE_MODE_2Y) { glLoadMatrixf(bgPanelStereo[0].transform.to_ptr()); glDrawArrays(GL_LINE_STRIP, 0, points.size() / 4); glLoadMatrixf(bgPanelStereo[1].transform.to_ptr()); glDrawArrays(GL_LINE_STRIP, points.size() / 4, points.size() / 4); } else if (scopeMode == SCOPE_MODE_XY) { glLoadMatrixf(bgPanel.transform.to_ptr()); glDrawArrays(GL_POINTS, 0, points.size() / 2); } glLineWidth(1.0); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_BLEND); } } CubicSDR-0.2.3/src/panel/ScopePanel.h000066400000000000000000000010441322677621400172130ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "GLPanel.h" class ScopePanel : public GLPanel { public: typedef enum ScopeMode { SCOPE_MODE_Y, SCOPE_MODE_2Y, SCOPE_MODE_XY } ScopeMode; ScopePanel(); void setMode(ScopeMode scopeMode); ScopeMode getMode(); void setPoints(std::vector &points); protected: void drawPanelContents(); private: std::vector points; ScopeMode scopeMode; GLPanel bgPanel; GLPanel bgPanelStereo[2]; };CubicSDR-0.2.3/src/panel/SpectrumPanel.cpp000066400000000000000000000231251322677621400203030ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SpectrumPanel.h" #include #include #include #include "CubicSDR.h" #include "ColorTheme.h" #include "CubicSDRDefs.h" SpectrumPanel::SpectrumPanel() { floorValue = 0; ceilValue = 1; showDb = true; useDbOfs = true; fftSize = DEFAULT_FFT_SIZE; bandwidth = DEFAULT_DEMOD_BW; freq = 0; setFill(GLPANEL_FILL_GRAD_Y); setFillColor(ThemeMgr::mgr.currentTheme->fftBackground * 2.0, ThemeMgr::mgr.currentTheme->fftBackground); dbPanelCeil.setMarginPx(0); dbPanelCeil.setFill(GLPanel::GLPANEL_FILL_GRAD_X); dbPanelCeil.setFillColor(RGBA4f(0.2f,0.2f,0.2f,5.0f), RGBA4f(0.2f,0.2f,0.2f,0.0f)); dbPanelFloor.setMarginPx(0); dbPanelFloor.setFill(GLPanel::GLPANEL_FILL_GRAD_X); dbPanelFloor.setFillColor(RGBA4f(0.2f,0.2f,0.2f,5.0f), RGBA4f(0.2f,0.2f,0.2f,0.0f)); } float SpectrumPanel::getFloorValue() { return floorValue; } void SpectrumPanel::setFloorValue(float floorValue) { this->floorValue = floorValue; } float SpectrumPanel::getCeilValue() { return ceilValue; } void SpectrumPanel::setCeilValue(float ceilValue) { this->ceilValue = ceilValue; } void SpectrumPanel::setFreq(long long freq) { this->freq = freq; } long long SpectrumPanel::getFreq() { return freq; } void SpectrumPanel::setBandwidth(long long bandwidth) { this->bandwidth = bandwidth; } long long SpectrumPanel::getBandwidth() { return bandwidth; } void SpectrumPanel::setFFTSize(int fftSize_in) { this->fftSize = fftSize_in; } int SpectrumPanel::getFFTSize() { return fftSize; } void SpectrumPanel::setShowDb(bool showDb) { this->showDb = showDb; if (showDb) { addChild(&dbPanelCeil); addChild(&dbPanelFloor); } else { removeChild(&dbPanelCeil); removeChild(&dbPanelFloor); } } bool SpectrumPanel::getShowDb() { return showDb; } void SpectrumPanel::setUseDBOffset(bool useOfs) { this->useDbOfs = useOfs; } bool SpectrumPanel::getUseDBOffset() { return useDbOfs; } void SpectrumPanel::setPoints(std::vector &points) { this->points.assign(points.begin(), points.end()); } void SpectrumPanel::setPeakPoints(std::vector &points) { this->peak_points.assign(points.begin(), points.end()); } void SpectrumPanel::drawPanelContents() { glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); glHint( GL_LINE_SMOOTH_HINT, GL_NICEST ); glLoadMatrixf((transform * (CubicVR::mat4::translate(-1.0f, -0.75f, 0.0f) * CubicVR::mat4::scale(2.0f, 1.5f, 1.0f))).to_ptr()); if (points.size()) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); double range = ceilValue-floorValue; double ranges[3][4] = { { 90.0, 5000.0, 10.0, 100.0 }, { 20.0, 150.0, 10.0, 10.0 }, { -20.0, 30.0, 10.0, 1.0 } }; for (int i = 0; i < 3; i++) { double p = 0; double rangeMin = ranges[i][0]; double rangeMax = ranges[i][1]; double rangeTrans = ranges[i][2]; double rangeStep = ranges[i][3]; if (range >= rangeMin && range <= rangeMax) { double a = 1.0; if (range <= rangeMin+rangeTrans) { a *= (range-rangeMin)/rangeTrans; } if (range >= rangeMax-rangeTrans) { a *= (rangeTrans-(range-(rangeMax-rangeTrans)))/rangeTrans; } glColor4f(0.12f, 0.12f, 0.12f, a); glBegin(GL_LINES); for (double l = floorValue; l<=ceilValue+rangeStep; l+=rangeStep) { p += rangeStep/range; glVertex2f(0,p); glVertex2f(1,p); } glEnd(); } } glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor3f(ThemeMgr::mgr.currentTheme->fftLine.r, ThemeMgr::mgr.currentTheme->fftLine.g, ThemeMgr::mgr.currentTheme->fftLine.b); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &points[0]); glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); if (peak_points.size()) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(0, 1.0, 0, 0.5); glVertexPointer(2, GL_FLOAT, 0, &peak_points[0]); glDrawArrays(GL_LINE_STRIP, 0, peak_points.size() / 2); } glDisableClientState(GL_VERTEX_ARRAY); } GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float viewWidth = (float) vp[2]; glLoadMatrixf(transform.to_ptr()); long long leftFreq = (double) freq - ((double) bandwidth / 2.0); long long rightFreq = leftFreq + (double) bandwidth; long long hzStep = 1000000; long double mhzStep = (100000.0 / (long double) (rightFreq - leftFreq)) * 2.0; double mhzVisualStep = 0.1; std::stringstream label; label.precision(1); double fontScale = GLFont::getScaleFactor(); if (mhzStep * 0.5 * viewWidth < 40 * fontScale) { mhzStep = (250000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 0.25; if (mhzStep * 0.5 * viewWidth < 40 * fontScale) { mhzStep = (500000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 0.5; } if (mhzStep * 0.5 * viewWidth < 40 * fontScale) { mhzStep = (1000000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 1.0; } if (mhzStep * 0.5 * viewWidth < 40 * fontScale) { mhzStep = (2500000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 2.5; } if (mhzStep * 0.5 * viewWidth < 40 * fontScale) { mhzStep = (5000000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 5.0; } if (mhzStep * 0.5 * viewWidth < 40 * fontScale) { mhzStep = (10000000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 10.0; } if (mhzStep * 0.5 * viewWidth < 40 * fontScale) { mhzStep = (50000000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 50.0; } } else if (mhzStep * 0.5 * viewWidth > 350 * fontScale) { mhzStep = (10000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 0.01; label.precision(2); } long long firstMhz = (leftFreq / hzStep) * hzStep; long double mhzStart = ((long double) (firstMhz - leftFreq) / (long double) (rightFreq - leftFreq)) * 2.0; long double currentMhz = trunc(floor(firstMhz / (long double)1000000.0)); double hPos = 1.0 - (16.0 / viewHeight) * GLFont::getScaleFactor(); double lMhzPos = 1.0 - (5.0 / viewHeight); int fontSize = 12; if (viewHeight > 135) { fontSize = 16; hPos = 1.0 - (18.0 / viewHeight) * GLFont::getScaleFactor(); } GLFont::Drawer refDrawingFont = GLFont::getFont(fontSize, GLFont::getScaleFactor()); for (double m = -1.0 + mhzStart, mMax = 1.0 + ((mhzStart>0)?mhzStart:-mhzStart); m <= mMax; m += mhzStep) { if (m < -1.0) { currentMhz += mhzVisualStep; continue; } if (m > 1.0) { break; } label << std::fixed << currentMhz; double fractpart, intpart; fractpart = modf(currentMhz, &intpart); if (fractpart < 0.001) { glLineWidth(4.0); glColor3f(ThemeMgr::mgr.currentTheme->freqLine.r, ThemeMgr::mgr.currentTheme->freqLine.g, ThemeMgr::mgr.currentTheme->freqLine.b); } else { glLineWidth(1.0); glColor3f(ThemeMgr::mgr.currentTheme->freqLine.r * 0.65, ThemeMgr::mgr.currentTheme->freqLine.g * 0.65, ThemeMgr::mgr.currentTheme->freqLine.b * 0.65); } glDisable(GL_TEXTURE_2D); glBegin(GL_LINES); glVertex2f(m, lMhzPos); glVertex2f(m, 1); glEnd(); glColor4f(ThemeMgr::mgr.currentTheme->text.r, ThemeMgr::mgr.currentTheme->text.g, ThemeMgr::mgr.currentTheme->text.b,1.0); refDrawingFont.drawString(label.str(), m, hPos, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); label.str(std::string()); currentMhz += mhzVisualStep; } glLineWidth(1.0); if (showDb) { float dbPanelWidth = (1.0 / viewWidth)*88.0 * GLFont::getScaleFactor(); float dbPanelHeight = (1.0/viewHeight)*14.0 * GLFont::getScaleFactor(); float dbOfs = useDbOfs?wxGetApp().getConfig()->getDBOffset():0; std::stringstream ssLabel(""); if (getCeilValue() != getFloorValue() && fftSize) { ssLabel << std::fixed << std::setprecision(1) << (dbOfs + 20.0 * log10(2.0*(getCeilValue())/(double)fftSize)) << "dB"; } dbPanelCeil.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT); dbPanelCeil.setSize(dbPanelWidth, dbPanelHeight); dbPanelCeil.setPosition(-1.0 + dbPanelWidth, 1.0 - dbPanelHeight); ssLabel.str(""); if (getCeilValue() != getFloorValue() && fftSize) { ssLabel << (dbOfs + 20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB"; } dbPanelFloor.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT); dbPanelFloor.setSize(dbPanelWidth, dbPanelHeight); dbPanelFloor.setPosition(-1.0 + dbPanelWidth, - 1.0 + dbPanelHeight); } } CubicSDR-0.2.3/src/panel/SpectrumPanel.h000066400000000000000000000020401322677621400177410ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "GLPanel.h" class SpectrumPanel : public GLPanel { public: SpectrumPanel(); void setPoints(std::vector &points); void setPeakPoints(std::vector &points); float getFloorValue(); void setFloorValue(float floorValue); float getCeilValue(); void setCeilValue(float ceilValue); void setFreq(long long freq); long long getFreq(); void setBandwidth(long long bandwidth); long long getBandwidth(); void setFFTSize(int fftSize_in); int getFFTSize(); void setShowDb(bool showDb); bool getShowDb(); void setUseDBOffset(bool useOfs); bool getUseDBOffset(); protected: void drawPanelContents(); private: float floorValue, ceilValue; int fftSize; long long freq; long long bandwidth; std::vector points; std::vector peak_points; GLTextPanel dbPanelCeil; GLTextPanel dbPanelFloor; bool showDb, useDbOfs; }; CubicSDR-0.2.3/src/panel/WaterfallPanel.cpp000066400000000000000000000164001322677621400204200ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "WaterfallPanel.h" WaterfallPanel::WaterfallPanel() : GLPanel(), fft_size(0), waterfall_lines(0), waterfall_slice(NULL), activeTheme(NULL) { setFillColor(RGBA4f(0,0,0)); for (int i = 0; i < 2; i++) { waterfall[i] = 0; } } void WaterfallPanel::setup(unsigned int fft_size_in, int num_waterfall_lines_in) { waterfall_lines = num_waterfall_lines_in; fft_size = fft_size_in; lines_buffered.store(0); if (points.size() != fft_size) { points.resize(fft_size); } texInitialized.store(false); bufferInitialized.store(false); } void WaterfallPanel::refreshTheme() { glEnable (GL_TEXTURE_2D); for (int i = 0; i < 2; i++) { glBindTexture(GL_TEXTURE_2D, waterfall[i]); glPixelTransferi(GL_MAP_COLOR, GL_TRUE); glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, &(ThemeMgr::mgr.currentTheme->waterfallGradient.getRed())[0]); glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 256, &(ThemeMgr::mgr.currentTheme->waterfallGradient.getGreen())[0]); glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, &(ThemeMgr::mgr.currentTheme->waterfallGradient.getBlue())[0]); } } void WaterfallPanel::setPoints(std::vector &points) { size_t halfPts = points.size()/2; if (halfPts == fft_size) { for (unsigned int i = 0; i < fft_size; i++) { this->points[i] = points[i*2+1]; } } else { this->points.assign(points.begin(), points.end()); } } void WaterfallPanel::step() { unsigned int half_fft_size = fft_size / 2; if (!bufferInitialized.load()) { delete waterfall_slice; waterfall_slice = new unsigned char[half_fft_size]; bufferInitialized.store(true); } if (!texInitialized.load()) { return; } if (points.size() && points.size() == fft_size) { for (int j = 0; j < 2; j++) { for (int i = 0, iMax = half_fft_size; i < iMax; i++) { float v = points[j * half_fft_size + i]; float wv = v < 0 ? 0 : (v > 0.99 ? 0.99 : v); waterfall_slice[i] = (unsigned char) floor(wv * 255.0); } unsigned int newBufSize = (half_fft_size*lines_buffered.load()+half_fft_size); if (lineBuffer[j].size() < newBufSize) { lineBuffer[j].resize(newBufSize); rLineBuffer[j].resize(newBufSize); } memcpy(&(lineBuffer[j][half_fft_size*lines_buffered.load()]), waterfall_slice, sizeof(unsigned char) * half_fft_size); } lines_buffered++; } } void WaterfallPanel::update() { int half_fft_size = fft_size / 2; if (!bufferInitialized.load()) { return; } if (!texInitialized.load()) { for (int i = 0; i < 2; i++) { if (waterfall[i]) { glDeleteTextures(1, &waterfall[i]); waterfall[i] = 0; } waterfall_ofs[i] = waterfall_lines - 1; } glGenTextures(2, waterfall); unsigned char *waterfall_tex; //Creates 2x 2D textures into card memory. //of size half_fft_size * waterfall_lines, which can be BIG. //The limit of the size of Waterfall is the size of the maximum supported 2D texture //by the graphic card. (half_fft_size * waterfall_lines, i.e DEFAULT_DEMOD_WATERFALL_LINES_NB * DEFAULT_FFT_SIZE/2) waterfall_tex = new unsigned char[half_fft_size * waterfall_lines]; memset(waterfall_tex, 0, half_fft_size * waterfall_lines); for (int i = 0; i < 2; i++) { glBindTexture(GL_TEXTURE_2D, waterfall[i]); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, half_fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); } delete[] waterfall_tex; refreshTheme(); texInitialized.store(true); } for (int i = 0, iMax = lines_buffered.load(); i < iMax; i++) { for (int j = 0; j < 2; j++) { memcpy(&(rLineBuffer[j][i*half_fft_size]), &(lineBuffer[j][((iMax-1)*half_fft_size)-(i*half_fft_size)]), sizeof(unsigned char) * half_fft_size); } } int run_ofs = 0; while (lines_buffered.load()) { int run_lines = lines_buffered.load(); if (run_lines > waterfall_ofs[0]) { run_lines = waterfall_ofs[0]; } for (int j = 0; j < 2; j++) { glBindTexture(GL_TEXTURE_2D, waterfall[j]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, waterfall_ofs[j]-run_lines, half_fft_size, run_lines, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) &(rLineBuffer[j][run_ofs])); waterfall_ofs[j]-=run_lines; if (waterfall_ofs[j] == 0) { waterfall_ofs[j] = waterfall_lines; } } run_ofs += run_lines*half_fft_size; lines_buffered.store(lines_buffered.load()-run_lines); } } void WaterfallPanel::drawPanelContents() { if (!texInitialized.load()) { return; } int half_fft_size = fft_size / 2; glLoadMatrixf(transform.to_ptr()); glEnable (GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); if (activeTheme != ThemeMgr::mgr.currentTheme) { refreshTheme(); activeTheme = ThemeMgr::mgr.currentTheme; } glColor3f(1.0, 1.0, 1.0); GLint vp[4]; glGetIntegerv(GL_VIEWPORT, vp); float viewWidth = (float) vp[2]; // some bias to prevent seams at odd scales float half_pixel = 1.0 / viewWidth; float half_texel = 1.0 / (float) half_fft_size; float vtexel = 1.0 / (float) waterfall_lines; float vofs = (float) (waterfall_ofs[0]) * vtexel; glBindTexture(GL_TEXTURE_2D, waterfall[0]); glBegin (GL_QUADS); glTexCoord2f(0.0 + half_texel, 1.0 + vofs); glVertex3f(-1.0, -1.0, 0.0); glTexCoord2f(1.0 - half_texel, 1.0 + vofs); glVertex3f(0.0 + half_pixel, -1.0, 0.0); glTexCoord2f(1.0 - half_texel, 0.0 + vofs); glVertex3f(0.0 + half_pixel, 1.0, 0.0); glTexCoord2f(0.0 + half_texel, 0.0 + vofs); glVertex3f(-1.0, 1.0, 0.0); glEnd(); vofs = (float) (waterfall_ofs[1]) * vtexel; glBindTexture(GL_TEXTURE_2D, waterfall[1]); glBegin(GL_QUADS); glTexCoord2f(0.0 + half_texel, 1.0 + vofs); glVertex3f(0.0 - half_pixel, -1.0, 0.0); glTexCoord2f(1.0 - half_texel, 1.0 + vofs); glVertex3f(1.0, -1.0, 0.0); glTexCoord2f(1.0 - half_texel, 0.0 + vofs); glVertex3f(1.0, 1.0, 0.0); glTexCoord2f(0.0 + half_texel, 0.0 + vofs); glVertex3f(0.0 - half_pixel, 1.0, 0.0); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glDisable(GL_TEXTURE_2D); } CubicSDR-0.2.3/src/panel/WaterfallPanel.h000066400000000000000000000014751322677621400200730ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "GLPanel.h" #include class WaterfallPanel : public GLPanel { public: WaterfallPanel(); void setup(unsigned int fft_size_in, int num_waterfall_lines_in); void refreshTheme(); void setPoints(std::vector &points); void step(); void update(); protected: void drawPanelContents(); private: std::vector points; GLuint waterfall[2]; int waterfall_ofs[2]; unsigned int fft_size; int waterfall_lines; unsigned char *waterfall_slice; std::vector lineBuffer[2]; std::vector rLineBuffer[2]; std::atomic_int lines_buffered; std::atomic_bool texInitialized, bufferInitialized; ColorTheme *activeTheme; }; CubicSDR-0.2.3/src/process/000077500000000000000000000000001322677621400153715ustar00rootroot00000000000000CubicSDR-0.2.3/src/process/FFTDataDistributor.cpp000066400000000000000000000127051322677621400215460ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "FFTDataDistributor.h" #include #include //50 ms #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) FFTDataDistributor::FFTDataDistributor() : outputBuffers("FFTDataDistributorBuffers"), fftSize(DEFAULT_FFT_SIZE), linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0) { } void FFTDataDistributor::setFFTSize(unsigned int size) { fftSize.store(size); } void FFTDataDistributor::setLinesPerSecond(unsigned int lines) { this->linesPerSecond = lines; } unsigned int FFTDataDistributor::getLinesPerSecond() { return this->linesPerSecond; } void FFTDataDistributor::process() { while (!input->empty()) { if (!isAnyOutputEmpty()) { return; } DemodulatorThreadIQDataPtr inp; if (!input->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) { continue; } if (inp) { //Settings have changed, set new values and dump all previous samples stored in inputBuffer: if (inputBuffer.sampleRate != inp->sampleRate || inputBuffer.frequency != inp->frequency) { //bufferMax must be at least fftSize (+ margin), else the waterfall get frozen, because no longer updated. bufferMax = std::max((size_t)(inp->sampleRate * FFT_DISTRIBUTOR_BUFFER_IN_SECONDS), (size_t)(1.2 * fftSize.load())); // std::cout << "Buffer Max: " << bufferMax << std::endl; bufferOffset = 0; bufferedItems = 0; inputBuffer.sampleRate = inp->sampleRate; inputBuffer.frequency = inp->frequency; inputBuffer.data.resize(bufferMax); } //adjust (bufferMax ; inputBuffer.data) in case of FFT size change only. if (bufferMax < (size_t)(1.2 * fftSize.load())) { bufferMax = (size_t)(1.2 * fftSize.load()); inputBuffer.data.resize(bufferMax); } size_t nbSamplesToAdd = inp->data.size(); //No room left in inputBuffer.data to accept inp->data.size() more samples. //so make room by sliding left of bufferOffset, which is fine because //those samples has already been processed. if ((bufferOffset + bufferedItems + inp->data.size()) > bufferMax) { memmove(&inputBuffer.data[0], &inputBuffer.data[bufferOffset], bufferedItems*sizeof(liquid_float_complex)); bufferOffset = 0; //if there are too much samples, we may even overflow ! //as a fallback strategy, drop the last incomming new samples not fitting in inputBuffer.data. if (bufferedItems + inp->data.size() > bufferMax) { //clamp nbSamplesToAdd nbSamplesToAdd = bufferMax - bufferedItems; std::cout << "FFTDataDistributor::process() incoming samples overflow, dropping the last " << (inp->data.size() - nbSamplesToAdd) << " input samples..." << std::endl; } } //store nbSamplesToAdd incoming samples. memcpy(&inputBuffer.data[bufferOffset+bufferedItems],&inp->data[0], nbSamplesToAdd *sizeof(liquid_float_complex)); bufferedItems += nbSamplesToAdd; // } else { //empty inp, wait for another. continue; } // number of seconds contained in input double inputTime = (double)bufferedItems / (double)inputBuffer.sampleRate; // number of lines in input double inputLines = (double)bufferedItems / (double)fftSize; // ratio required to achieve the desired rate: // it means we can achieive 'lineRateStep' times the target linesPerSecond. // < 1 means we cannot reach it by lack of samples. double lineRateStep = ((double)linesPerSecond * inputTime)/(double)inputLines; //we have enough samples to FFT at least one 'line' of 'fftSize' frequencies for display: if (bufferedItems >= fftSize) { size_t numProcessed = 0; if (lineRateAccum + (lineRateStep * ((double)bufferedItems/(double)fftSize)) < 1.0) { // move along, nothing to see here.. lineRateAccum += (lineRateStep * ((double)bufferedItems/(double)fftSize)); numProcessed = bufferedItems; } else { for (size_t i = 0, iMax = bufferedItems; i < iMax; i += fftSize) { if ((i + fftSize) > iMax) { break; } lineRateAccum += lineRateStep; if (lineRateAccum >= 1.0) { //each i represents a FFT computation DemodulatorThreadIQDataPtr outp = outputBuffers.getBuffer(); outp->frequency = inputBuffer.frequency; outp->sampleRate = inputBuffer.sampleRate; outp->data.assign(inputBuffer.data.begin()+bufferOffset+i, inputBuffer.data.begin()+bufferOffset+i+ fftSize); //authorize distribute with losses distribute(outp, NON_BLOCKING_TIMEOUT); while (lineRateAccum >= 1.0) { lineRateAccum -= 1.0; } } numProcessed += fftSize; } //end for } //advance bufferOffset read pointer, //reduce size of bufferedItems. if (numProcessed) { bufferedItems -= numProcessed; bufferOffset += numProcessed; } //clamp to zero the number of remaining items. if (bufferedItems <= 0) { bufferedItems = 0; bufferOffset = 0; } } //end if bufferedItems >= fftSize } //en while } CubicSDR-0.2.3/src/process/FFTDataDistributor.h000066400000000000000000000014351322677621400212110ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "VisualProcessor.h" #include "DemodDefs.h" #include #include #include class FFTDataDistributor : public VisualProcessor { public: FFTDataDistributor(); void setFFTSize(unsigned int size); void setLinesPerSecond(unsigned int lines); unsigned int getLinesPerSecond(); protected: virtual void process(); DemodulatorThreadIQData inputBuffer, tempBuffer; ReBuffer outputBuffers; std::atomic fftSize; unsigned int linesPerSecond; double lineRateAccum; size_t bufferMax = 0; size_t bufferOffset = 0; size_t bufferedItems = 0; }; CubicSDR-0.2.3/src/process/FFTVisualDataThread.cpp000066400000000000000000000054151322677621400216270ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "FFTVisualDataThread.h" #include "CubicSDR.h" FFTVisualDataThread::FFTVisualDataThread() { linesPerSecond.store(DEFAULT_WATERFALL_LPS); lpsChanged.store(true); } FFTVisualDataThread::~FFTVisualDataThread() { } void FFTVisualDataThread::setLinesPerSecond(int lps) { linesPerSecond.store(lps); lpsChanged.store(true); } int FFTVisualDataThread::getLinesPerSecond() { return linesPerSecond.load(); } SpectrumVisualProcessor *FFTVisualDataThread::getProcessor() { return &wproc; } void FFTVisualDataThread::run() { DemodulatorThreadInputQueuePtr pipeIQDataIn = std::static_pointer_cast(getInputQueue("IQDataInput")); SpectrumVisualDataQueuePtr pipeFFTDataOut = std::static_pointer_cast(getOutputQueue("FFTDataOutput")); fftQueue->set_max_num_items(100); pipeFFTDataOut->set_max_num_items(100); //FFT distributor plumbing: // IQDataInput push samples to process to FFT Data distributor. fftDistrib.setInput(pipeIQDataIn); //The FFT distributor has actually 1 output only, so it doesn't distribute at all :) fftDistrib.attachOutput(fftQueue); //FFT Distributor output is ==> SpectrumVisualProcessor input. wproc.setInput(fftQueue); wproc.attachOutput(pipeFFTDataOut); wproc.setup(DEFAULT_FFT_SIZE); // std::cout << "FFT visual data thread started." << std::endl; while(!stopping) { //this if fed by FFTDataDistributor which has a buffer of FFT_DISTRIBUTOR_BUFFER_IN_SECONDS //so sleep for << FFT_DISTRIBUTOR_BUFFER_IN_SECONDS not to be overflown std::this_thread::sleep_for(std::chrono::milliseconds((int)(FFT_DISTRIBUTOR_BUFFER_IN_SECONDS * 1000.0 / 25.0))); int fftSize = wproc.getDesiredInputSize(); if (fftSize) { fftDistrib.setFFTSize(fftSize); } else { fftDistrib.setFFTSize(DEFAULT_FFT_SIZE * SPECTRUM_VZM); } if (lpsChanged.load()) { fftDistrib.setLinesPerSecond(linesPerSecond.load()); lpsChanged.store(false); } //Make FFT Distributor process IQ samples //and package them into ready-to-FFT sample sets (representing 1 line) by wproc fftDistrib.run(); // Make wproc do a FFT of each of the sample sets provided by fftDistrib: while (!stopping && !wproc.isInputEmpty()) { wproc.run(); } } pipeIQDataIn->flush(); pipeFFTDataOut->flush(); // std::cout << "FFT visual data thread done." << std::endl; } void FFTVisualDataThread::terminate() { IOThread::terminate(); fftDistrib.flushQueues(); wproc.flushQueues(); } CubicSDR-0.2.3/src/process/FFTVisualDataThread.h000066400000000000000000000013431322677621400212700ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include "IOThread.h" #include "SpectrumVisualProcessor.h" #include "FFTDataDistributor.h" class FFTVisualDataThread : public IOThread { public: FFTVisualDataThread(); ~FFTVisualDataThread(); void setLinesPerSecond(int lps); int getLinesPerSecond(); SpectrumVisualProcessor *getProcessor(); virtual void run(); virtual void terminate(); protected: FFTDataDistributor fftDistrib; DemodulatorThreadInputQueuePtr fftQueue = std::make_shared(); SpectrumVisualProcessor wproc; std::atomic_int linesPerSecond; std::atomic_bool lpsChanged; }; CubicSDR-0.2.3/src/process/ScopeVisualProcessor.cpp000066400000000000000000000175341322677621400222440ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ScopeVisualProcessor.h" #include #include ScopeVisualProcessor::ScopeVisualProcessor(): outputBuffers("ScopeVisualProcessorBuffers") { scopeEnabled.store(true); spectrumEnabled.store(true); fft_average_rate = 0.65f; fft_ceil_ma = fft_ceil_maa = 0; fft_floor_ma = fft_floor_maa = 0; maxScopeSamples = DEFAULT_DMOD_FFT_SIZE; fftPlan = nullptr; } ScopeVisualProcessor::~ScopeVisualProcessor() { if (fftPlan) { fft_destroy_plan(fftPlan); } } void ScopeVisualProcessor::setup(int fftSize_in) { fftSize = fftSize_in; desiredInputSize = fftSize; fftInData.resize(fftSize); fftOutput.resize(fftSize); if (fftPlan) { fft_destroy_plan(fftPlan); } fftPlan = fft_create_plan(fftSize, fftInData.data(), fftOutput.data(), LIQUID_FFT_FORWARD, 0); } void ScopeVisualProcessor::setScopeEnabled(bool scopeEnable) { scopeEnabled.store(scopeEnable); } void ScopeVisualProcessor::setSpectrumEnabled(bool spectrumEnable) { spectrumEnabled.store(spectrumEnable); } void ScopeVisualProcessor::process() { if (!isOutputEmpty()) { return; } AudioThreadInputPtr audioInputData; if (input->try_pop(audioInputData)) { if (!audioInputData) { return; } size_t i, iMax = audioInputData->data.size(); if (!iMax) { //discard audioInputData. audioInputData = nullptr; return; } ScopeRenderDataPtr renderData = nullptr; if (scopeEnabled) { iMax = audioInputData->data.size(); if (iMax > maxScopeSamples) { iMax = maxScopeSamples; } renderData = outputBuffers.getBuffer(); renderData->channels = audioInputData->channels; renderData->inputRate = audioInputData->inputRate; renderData->sampleRate = audioInputData->sampleRate; if (renderData->waveform_points.size() != iMax * 2) { renderData->waveform_points.resize(iMax * 2); } float peak = 1.0f; for (i = 0; i < iMax; i++) { float p = fabs(audioInputData->data[i]); if (p > peak) { peak = p; } } if (audioInputData->type == 1) { iMax = audioInputData->data.size(); if (renderData->waveform_points.size() != iMax * 2) { renderData->waveform_points.resize(iMax * 2); } for (i = 0; i < iMax; i++) { renderData->waveform_points[i * 2] = (((double) (i % (iMax/2)) / (double) iMax) * 2.0 - 0.5) * 2.0; renderData->waveform_points[i * 2 + 1] = audioInputData->data[i] / peak; } renderData->mode = ScopePanel::SCOPE_MODE_2Y; } else if (audioInputData->type == 2) { iMax = audioInputData->data.size(); if (renderData->waveform_points.size() != iMax) { renderData->waveform_points.resize(iMax); } for (i = 0; i < iMax/2; i++) { renderData->waveform_points[i * 2] = audioInputData->data[i * 2] / peak; renderData->waveform_points[i * 2 + 1] = audioInputData->data[i * 2 + 1] / peak; } renderData->mode = ScopePanel::SCOPE_MODE_XY; } else { for (i = 0; i < iMax; i++) { renderData->waveform_points[i * 2] = (((double) i / (double) iMax) - 0.5) * 2.0; renderData->waveform_points[i * 2 + 1] = audioInputData->data[i] / peak; } renderData->mode = ScopePanel::SCOPE_MODE_Y; } renderData->spectrum = false; distribute(renderData); } if (spectrumEnabled) { iMax = audioInputData->data.size(); if (audioInputData->channels==1) { for (i = 0; i < fftSize; i++) { if (i < iMax) { fftInData[i].real = audioInputData->data[i]; fftInData[i].imag = 0; } else { fftInData[i].real = 0; fftInData[i].imag = 0; } } } else if (audioInputData->channels==2) { iMax = iMax/2; for (i = 0; i < fftSize; i++) { if (i < iMax) { fftInData[i].real = audioInputData->data[i] + audioInputData->data[iMax+i]; fftInData[i].imag = 0; } else { fftInData[i].real = 0; fftInData[i].imag = 0; } } } renderData = outputBuffers.getBuffer(); renderData->channels = audioInputData->channels; renderData->inputRate = audioInputData->inputRate; renderData->sampleRate = audioInputData->sampleRate; audioInputData = nullptr; //->decRefCount(); double fft_ceil = 0, fft_floor = 1; if (fft_result.size() < (fftSize/2)) { fft_result.resize((fftSize/2)); fft_result_ma.resize((fftSize/2)); fft_result_maa.resize((fftSize/2)); } fft_execute(fftPlan); for (i = 0; i < (fftSize/2); i++) { //cast result to double to prevent overflows / excessive precision losses in the following computations... double a = (double) fftOutput[i].real; double b = (double) fftOutput[i].imag; //computes norm = sqrt(a**2 + b**2) //being actually floats cast into doubles, we are indeed overflow-free here. fft_result[i] = sqrt(a*a + b*b); } for (i = 0; i < (fftSize/2); i++) { fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; if (fft_result_maa[i] > fft_ceil) { fft_ceil = fft_result_maa[i]; } if (fft_result_maa[i] < fft_floor) { fft_floor = fft_result_maa[i]; } } fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; unsigned int outSize = fftSize/2; if (renderData->sampleRate != renderData->inputRate) { outSize = (int)floor((float)outSize * ((float)renderData->sampleRate/(float)renderData->inputRate)); } if (renderData->waveform_points.size() != outSize*2) { renderData->waveform_points.resize(outSize*2); } for (i = 0; i < outSize; i++) { float v = (log10(fft_result_maa[i]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))); renderData->waveform_points[i * 2] = ((double) i / (double) (outSize)); renderData->waveform_points[i * 2 + 1] = v; } renderData->fft_floor = fft_floor_maa; renderData->fft_ceil = fft_ceil_maa; renderData->fft_size = fftSize/2; renderData->spectrum = true; distribute(renderData); } } //end if try_pop() } CubicSDR-0.2.3/src/process/ScopeVisualProcessor.h000066400000000000000000000030511322677621400216760ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "VisualProcessor.h" #include "AudioThread.h" #include "ScopePanel.h" #include class ScopeRenderData { public: std::vector waveform_points; ScopePanel::ScopeMode mode = ScopePanel::SCOPE_MODE_Y; int inputRate; int sampleRate; int channels; bool spectrum; int fft_size; double fft_floor, fft_ceil; virtual ~ScopeRenderData() { } }; typedef std::shared_ptr ScopeRenderDataPtr; typedef ThreadBlockingQueue ScopeRenderDataQueue; typedef std::shared_ptr ScopeRenderDataQueuePtr; class ScopeVisualProcessor : public VisualProcessor { public: ScopeVisualProcessor(); ~ScopeVisualProcessor(); void setup(int fftSize_in); void setScopeEnabled(bool scopeEnable); void setSpectrumEnabled(bool spectrumEnable); protected: virtual void process(); ReBuffer outputBuffers; std::atomic_bool scopeEnabled; std::atomic_bool spectrumEnabled; std::vector fftInData; std::vector fftOutput; fftplan fftPlan; unsigned int fftSize = 0; int desiredInputSize; unsigned int maxScopeSamples; double fft_ceil_ma, fft_ceil_maa; double fft_floor_ma, fft_floor_maa; double fft_average_rate; std::vector fft_result; std::vector fft_result_ma; std::vector fft_result_maa; }; CubicSDR-0.2.3/src/process/SpectrumVisualDataThread.cpp000066400000000000000000000016251322677621400230110ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SpectrumVisualDataThread.h" #include "CubicSDR.h" SpectrumVisualDataThread::SpectrumVisualDataThread() { } SpectrumVisualDataThread::~SpectrumVisualDataThread() { } SpectrumVisualProcessor *SpectrumVisualDataThread::getProcessor() { return &sproc; } void SpectrumVisualDataThread::run() { while(!stopping) { //this if fed by FFTDataDistributor which has a buffer of FFT_DISTRIBUTOR_BUFFER_IN_SECONDS //so sleep for << FFT_DISTRIBUTOR_BUFFER_IN_SECONDS not to be overflown std::this_thread::sleep_for(std::chrono::milliseconds((int)(FFT_DISTRIBUTOR_BUFFER_IN_SECONDS * 1000.0 / 25.0))); sproc.run(); } // std::cout << "Spectrum visual data thread done." << std::endl; } void SpectrumVisualDataThread::terminate() { IOThread::terminate(); sproc.flushQueues(); }CubicSDR-0.2.3/src/process/SpectrumVisualDataThread.h000066400000000000000000000006561322677621400224610ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "IOThread.h" #include "SpectrumVisualProcessor.h" class SpectrumVisualDataThread : public IOThread { public: SpectrumVisualDataThread(); ~SpectrumVisualDataThread(); SpectrumVisualProcessor *getProcessor(); virtual void run(); virtual void terminate(); protected: SpectrumVisualProcessor sproc; }; CubicSDR-0.2.3/src/process/SpectrumVisualProcessor.cpp000066400000000000000000000574531322677621400230010ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SpectrumVisualProcessor.h" #include "CubicSDR.h" //50 ms #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) SpectrumVisualProcessor::SpectrumVisualProcessor() : outputBuffers("SpectrumVisualProcessorBuffers") { lastInputBandwidth = 0; lastBandwidth = 0; lastDataSize = 0; resampler = nullptr; resamplerRatio = 0; fftInput = nullptr; fftOutput = nullptr; fftInData = nullptr; fftLastData = nullptr; fftPlan = nullptr; is_view = false; fftSize = 0; centerFreq = 0; bandwidth = 0; hideDC = false; freqShifter = nco_crcf_create(LIQUID_NCO); shiftFrequency = 0; fft_ceil_ma = fft_ceil_maa = 100.0; fft_floor_ma = fft_floor_maa = 0.0; fft_floor_peak = 0.0; desiredInputSize = 0; fft_average_rate = 0.65f; scaleFactor = 1.0; fftSizeChanged = false; newFFTSize = 0; lastView = false; peakHold = false; peakReset = false; } SpectrumVisualProcessor::~SpectrumVisualProcessor() { nco_crcf_destroy(freqShifter); } bool SpectrumVisualProcessor::isView() { std::lock_guard < std::mutex > busy_lock(busy_run); return is_view; } void SpectrumVisualProcessor::setView(bool bView) { std::lock_guard < std::mutex > busy_lock(busy_run); is_view = bView; } void SpectrumVisualProcessor::setView(bool bView, long long centerFreq_in, long bandwidth_in) { std::lock_guard < std::mutex > busy_lock(busy_run); is_view = bView; bandwidth = bandwidth_in; centerFreq = centerFreq_in; } void SpectrumVisualProcessor::setFFTAverageRate(float fftAverageRate) { std::lock_guard < std::mutex > busy_lock(busy_run); this->fft_average_rate = fftAverageRate; } float SpectrumVisualProcessor::getFFTAverageRate() { std::lock_guard < std::mutex > busy_lock(busy_run); return this->fft_average_rate; } void SpectrumVisualProcessor::setCenterFrequency(long long centerFreq_in) { std::lock_guard < std::mutex > busy_lock(busy_run); centerFreq = centerFreq_in; } long long SpectrumVisualProcessor::getCenterFrequency() { std::lock_guard < std::mutex > busy_lock(busy_run); return centerFreq; } void SpectrumVisualProcessor::setBandwidth(long bandwidth_in) { std::lock_guard < std::mutex > busy_lock(busy_run); bandwidth = bandwidth_in; } long SpectrumVisualProcessor::getBandwidth() { std::lock_guard < std::mutex > busy_lock(busy_run); return bandwidth; } void SpectrumVisualProcessor::setPeakHold(bool peakHold_in) { std::lock_guard < std::mutex > busy_lock(busy_run); if (peakHold && peakHold_in) { peakReset = PEAK_RESET_COUNT; } else { peakHold = peakHold_in; peakReset = 1; } } bool SpectrumVisualProcessor::getPeakHold() { std::lock_guard < std::mutex > busy_lock(busy_run); return peakHold; } int SpectrumVisualProcessor::getDesiredInputSize() { std::lock_guard < std::mutex > busy_lock(busy_run); return desiredInputSize; } void SpectrumVisualProcessor::setup(unsigned int fftSize_in) { std::lock_guard < std::mutex > busy_lock(busy_run); fftSize = fftSize_in; fftSizeInternal = fftSize_in * SPECTRUM_VZM; lastDataSize = 0; int memSize = sizeof(liquid_float_complex) * fftSizeInternal; if (fftInput) { free(fftInput); } fftInput = (liquid_float_complex*)malloc(memSize); memset(fftInput,0,memSize); if (fftInData) { free(fftInData); } fftInData = (liquid_float_complex*)malloc(memSize); memset(fftInput,0,memSize); if (fftLastData) { free(fftLastData); } fftLastData = (liquid_float_complex*)malloc(memSize); memset(fftInput,0,memSize); if (fftOutput) { free(fftOutput); } fftOutput = (liquid_float_complex*)malloc(memSize); memset(fftInput,0,memSize); if (fftPlan) { fft_destroy_plan(fftPlan); } fftPlan = fft_create_plan(fftSizeInternal, fftInput, fftOutput, LIQUID_FFT_FORWARD, 0); } void SpectrumVisualProcessor::setFFTSize(unsigned int fftSize_in) { //then get the busy_lock std::lock_guard < std::mutex > busy_lock(busy_run); if (fftSize_in == fftSize) { return; } newFFTSize = fftSize_in; fftSizeChanged = true; } unsigned int SpectrumVisualProcessor::getFFTSize() { //then get the busy_lock std::lock_guard < std::mutex > busy_lock(busy_run); if (fftSizeChanged) { return newFFTSize; } return fftSize; } void SpectrumVisualProcessor::setHideDC(bool hideDC) { std::lock_guard < std::mutex > busy_lock(busy_run); this->hideDC = hideDC; } void SpectrumVisualProcessor::process() { if (!isOutputEmpty()) { return; } if (!input || input->empty()) { return; } bool executeSetup = false; { // scoped lock here std::lock_guard < std::mutex > busy_lock(busy_run); if (fftSizeChanged) { executeSetup = true; fftSizeChanged = false; } } if (executeSetup) { setup(newFFTSize); } DemodulatorThreadIQDataPtr iqData; if (!input->pop(iqData, HEARTBEAT_CHECK_PERIOD_MICROS)) { return; } if (!iqData) { return; } //then get the busy_lock for the rest of the processing. std::lock_guard < std::mutex > busy_lock(busy_run); bool doPeak = peakHold && (peakReset == 0); if (fft_result.size() != fftSizeInternal) { if (fft_result.capacity() < fftSizeInternal) { fft_result.reserve(fftSizeInternal); fft_result_ma.reserve(fftSizeInternal); fft_result_maa.reserve(fftSizeInternal); fft_result_peak.reserve(fftSizeInternal); } fft_result.resize(fftSizeInternal); fft_result_ma.resize(fftSizeInternal); fft_result_maa.resize(fftSizeInternal); fft_result_temp.resize(fftSizeInternal); fft_result_peak.resize(fftSizeInternal); } if (peakReset != 0) { peakReset--; if (peakReset == 0) { for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_peak[i] = fft_floor_maa; } fft_ceil_peak = fft_floor_maa; fft_floor_peak = fft_ceil_maa; } } std::vector *data = &iqData->data; if (data && data->size()) { unsigned int num_written; long resampleBw = iqData->sampleRate; bool newResampler = false; int bwDiff = 0; if (is_view) { if (!iqData->sampleRate) { return; } while (resampleBw / SPECTRUM_VZM >= bandwidth) { resampleBw /= SPECTRUM_VZM; } resamplerRatio = (double) (resampleBw) / (double) iqData->sampleRate; size_t desired_input_size = fftSizeInternal / resamplerRatio; this->desiredInputSize = desired_input_size; if (iqData->data.size() < desired_input_size) { // std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl; desired_input_size = iqData->data.size(); } if (centerFreq != iqData->frequency) { if ((centerFreq - iqData->frequency) != shiftFrequency || lastInputBandwidth != iqData->sampleRate) { if (abs(iqData->frequency - centerFreq) < (wxGetApp().getSampleRate() / 2)) { long lastShiftFrequency = shiftFrequency; shiftFrequency = centerFreq - iqData->frequency; nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) iqData->sampleRate))); if (is_view) { long freqDiff = shiftFrequency - lastShiftFrequency; if (lastBandwidth!=0) { double binPerHz = double(lastBandwidth) / double(fftSizeInternal); unsigned int numShift = floor(double(abs(freqDiff)) / binPerHz); if (numShift < fftSizeInternal/2 && numShift) { if (freqDiff > 0) { memmove(&fft_result_ma[0], &fft_result_ma[numShift], (fftSizeInternal-numShift) * sizeof(double)); memmove(&fft_result_maa[0], &fft_result_maa[numShift], (fftSizeInternal-numShift) * sizeof(double)); // memmove(&fft_result_peak[0], &fft_result_peak[numShift], (fftSizeInternal-numShift) * sizeof(double)); // memset(&fft_result_peak[fftSizeInternal-numShift], 0, numShift * sizeof(double)); } else { memmove(&fft_result_ma[numShift], &fft_result_ma[0], (fftSizeInternal-numShift) * sizeof(double)); memmove(&fft_result_maa[numShift], &fft_result_maa[0], (fftSizeInternal-numShift) * sizeof(double)); // memmove(&fft_result_peak[numShift], &fft_result_peak[0], (fftSizeInternal-numShift) * sizeof(double)); // memset(&fft_result_peak[0], 0, numShift * sizeof(double)); } } } } } peakReset = PEAK_RESET_COUNT; } if (shiftBuffer.size() != desired_input_size) { if (shiftBuffer.capacity() < desired_input_size) { shiftBuffer.reserve(desired_input_size); } shiftBuffer.resize(desired_input_size); } if (shiftFrequency < 0) { nco_crcf_mix_block_up(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size); } else { nco_crcf_mix_block_down(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size); } } else { shiftBuffer.assign(iqData->data.begin(), iqData->data.begin()+desired_input_size); } if (!resampler || resampleBw != lastBandwidth || lastInputBandwidth != iqData->sampleRate) { float As = 480.0; if (resampler) { msresamp_crcf_destroy(resampler); } resampler = msresamp_crcf_create(resamplerRatio, As); bwDiff = resampleBw-lastBandwidth; lastBandwidth = resampleBw; lastInputBandwidth = iqData->sampleRate; newResampler = true; peakReset = PEAK_RESET_COUNT; } unsigned int out_size = ceil((double) (desired_input_size) * resamplerRatio) + 512; if (resampleBuffer.size() != out_size) { if (resampleBuffer.capacity() < out_size) { resampleBuffer.reserve(out_size); } resampleBuffer.resize(out_size); } msresamp_crcf_execute(resampler, &shiftBuffer[0], desired_input_size, &resampleBuffer[0], &num_written); if (num_written < fftSizeInternal) { memcpy(fftInData, resampleBuffer.data(), num_written * sizeof(liquid_float_complex)); memset(&(fftInData[num_written]), 0, (fftSizeInternal-num_written) * sizeof(liquid_float_complex)); } else { memcpy(fftInData, resampleBuffer.data(), fftSizeInternal * sizeof(liquid_float_complex)); } } else { this->desiredInputSize = fftSizeInternal; num_written = data->size(); if (data->size() < fftSizeInternal) { memcpy(fftInData, data->data(), data->size() * sizeof(liquid_float_complex)); memset(&fftInData[data->size()], 0, (fftSizeInternal - data->size()) * sizeof(liquid_float_complex)); } else { memcpy(fftInData, data->data(), fftSizeInternal * sizeof(liquid_float_complex)); } } bool execute = false; if (num_written >= fftSizeInternal) { execute = true; memcpy(fftInput, fftInData, fftSizeInternal * sizeof(liquid_float_complex)); memcpy(fftLastData, fftInput, fftSizeInternal * sizeof(liquid_float_complex)); } else { if (lastDataSize + num_written < fftSizeInternal) { // priming unsigned int num_copy = fftSizeInternal - lastDataSize; if (num_written > num_copy) { num_copy = num_written; } memcpy(fftLastData, fftInData, num_copy * sizeof(liquid_float_complex)); lastDataSize += num_copy; } else { unsigned int num_last = (fftSizeInternal - num_written); memcpy(fftInput, fftLastData + (lastDataSize - num_last), num_last * sizeof(liquid_float_complex)); memcpy(fftInput + num_last, fftInData, num_written * sizeof(liquid_float_complex)); memcpy(fftLastData, fftInput, fftSizeInternal * sizeof(liquid_float_complex)); execute = true; } } if (execute) { SpectrumVisualDataPtr output = outputBuffers.getBuffer(); if (output->spectrum_points.size() != fftSize * 2) { output->spectrum_points.resize(fftSize * 2); } if (doPeak) { if (output->spectrum_hold_points.size() != fftSize * 2) { output->spectrum_hold_points.resize(fftSize * 2); } } else { output->spectrum_hold_points.resize(0); } float fft_ceil = 0, fft_floor = 1; fft_execute(fftPlan); for (int i = 0, iMax = fftSizeInternal / 2; i < iMax; i++) { float a = fftOutput[i].real; float b = fftOutput[i].imag; float c = sqrt(a * a + b * b); float x = fftOutput[fftSizeInternal / 2 + i].real; float y = fftOutput[fftSizeInternal / 2 + i].imag; float z = sqrt(x * x + y * y); fft_result[i] = (z); fft_result[fftSizeInternal / 2 + i] = (c); } if (newResampler && lastView) { if (bwDiff < 0) { for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_temp[i] = fft_result_ma[(fftSizeInternal/4) + (i/2)]; } for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_ma[i] = fft_result_temp[i]; fft_result_temp[i] = fft_result_maa[(fftSizeInternal/4) + (i/2)]; } for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_maa[i] = fft_result_temp[i]; } } else { for (size_t i = 0, iMax = fftSizeInternal; i < iMax; i++) { if (i < fftSizeInternal/4) { fft_result_temp[i] = 0; // fft_result_ma[fftSizeInternal/4]; } else if (i >= fftSizeInternal - fftSizeInternal/4) { fft_result_temp[i] = 0; // fft_result_ma[fftSizeInternal - fftSizeInternal/4-1]; } else { fft_result_temp[i] = fft_result_ma[(i-fftSizeInternal/4)*2]; } } for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_ma[i] = fft_result_temp[i]; if (i < fftSizeInternal/4) { fft_result_temp[i] = 0; //fft_result_maa[fftSizeInternal/4]; } else if (i >= fftSizeInternal - fftSizeInternal/4) { fft_result_temp[i] = 0; // fft_result_maa[fftSizeInternal - fftSizeInternal/4-1]; } else { fft_result_temp[i] = fft_result_maa[(i-fftSizeInternal/4)*2]; } } for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_maa[i] = fft_result_temp[i]; } } } for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) { if (fft_result_maa[i] != fft_result_maa[i]) fft_result_maa[i] = fft_result[i]; fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; if (fft_result_ma[i] != fft_result_ma[i]) fft_result_ma[i] = fft_result[i]; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; if (fft_result_maa[i] > fft_ceil || fft_ceil != fft_ceil) { fft_ceil = fft_result_maa[i]; } if (fft_result_maa[i] < fft_floor || fft_floor != fft_floor) { fft_floor = fft_result_maa[i]; } if (doPeak) { if (fft_result_maa[i] > fft_result_peak[i]) { fft_result_peak[i] = fft_result_maa[i]; } } } if (fft_ceil_ma != fft_ceil_ma) fft_ceil_ma = fft_ceil; fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; if (fft_ceil_maa != fft_ceil_maa) fft_ceil_maa = fft_ceil; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; if (fft_floor_ma != fft_floor_ma) fft_floor_ma = fft_floor; fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; if (fft_floor_maa != fft_floor_maa) fft_floor_maa = fft_floor; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; if (doPeak) { if (fft_ceil_maa > fft_ceil_peak) { fft_ceil_peak = fft_ceil_maa; } if (fft_floor_maa < fft_floor_peak) { fft_floor_peak = fft_floor_maa; } } float sf = scaleFactor; double visualRatio = (double(bandwidth) / double(resampleBw)); double visualStart = (double(fftSizeInternal) / 2.0) - (double(fftSizeInternal) * (visualRatio / 2.0)); double visualAccum = 0; double peak_acc = 0, acc = 0, accCount = 0, i = 0; double point_ceil = doPeak?fft_ceil_peak:fft_ceil_maa; double point_floor = doPeak?fft_floor_peak:fft_floor_maa; for (int x = 0, xMax = output->spectrum_points.size() / 2; x < xMax; x++) { visualAccum += visualRatio * double(SPECTRUM_VZM); while (visualAccum >= 1.0) { unsigned int idx = round(visualStart+i); if (idx > 0 && idx < fftSizeInternal) { acc += fft_result_maa[idx]; if (doPeak) { peak_acc += fft_result_peak[idx]; } } else { acc += fft_floor_maa; if (doPeak) { peak_acc += fft_floor_maa; } } accCount += 1.0; visualAccum -= 1.0; i++; } output->spectrum_points[x * 2] = ((float) x / (float) xMax); if (doPeak) { output->spectrum_hold_points[x * 2] = ((float) x / (float) xMax); } if (accCount) { output->spectrum_points[x * 2 + 1] = ((log10((acc/accCount)+0.25 - (point_floor-0.75)) / log10((point_ceil+0.25) - (point_floor-0.75))))*sf; acc = 0.0; if (doPeak) { output->spectrum_hold_points[x * 2 + 1] = ((log10((peak_acc/accCount)+0.25 - (point_floor-0.75)) / log10((point_ceil+0.25) - (point_floor-0.75))))*sf; peak_acc = 0.0; } accCount = 0.0; } } if (hideDC) { // DC-spike removal long long freqMin = centerFreq-(bandwidth/2); long long freqMax = centerFreq+(bandwidth/2); long long zeroPt = (iqData->frequency-freqMin); if (freqMin < iqData->frequency && freqMax > iqData->frequency) { int freqRange = int(freqMax-freqMin); int freqStep = freqRange/fftSize; int fftStart = (zeroPt/freqStep)-(2000/freqStep); int fftEnd = (zeroPt/freqStep)+(2000/freqStep); // std::cout << "range:" << freqRange << ", step: " << freqStep << ", start: " << fftStart << ", end: " << fftEnd << std::endl; if (fftEnd-fftStart < 2) { fftEnd++; fftStart--; } int numSteps = (fftEnd-fftStart); int halfWay = fftStart+(numSteps/2); if ((fftEnd+numSteps/2+1 < (long long) fftSize) && (fftStart-numSteps/2-1 >= 0) && (fftEnd > fftStart)) { int n = 1; for (int i = fftStart; i < halfWay; i++) { output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftStart - n) * 2 + 1]; n++; } n = 1; for (int i = halfWay; i < fftEnd; i++) { output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftEnd + n) * 2 + 1]; n++; } if (doPeak) { int n = 1; for (int i = fftStart; i < halfWay; i++) { output->spectrum_hold_points[i * 2 + 1] = output->spectrum_hold_points[(fftStart - n) * 2 + 1]; n++; } n = 1; for (int i = halfWay; i < fftEnd; i++) { output->spectrum_hold_points[i * 2 + 1] = output->spectrum_hold_points[(fftEnd + n) * 2 + 1]; n++; } } } } } output->fft_ceiling = point_ceil/sf; output->fft_floor = point_floor; output->centerFreq = centerFreq; output->bandwidth = bandwidth; distribute(output); } } lastView = is_view; } void SpectrumVisualProcessor::setScaleFactor(float sf) { std::lock_guard < std::mutex > busy_lock(busy_run); scaleFactor = sf; } float SpectrumVisualProcessor::getScaleFactor() { std::lock_guard < std::mutex > busy_lock(busy_run); return scaleFactor; } CubicSDR-0.2.3/src/process/SpectrumVisualProcessor.h000066400000000000000000000053211322677621400224310ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "VisualProcessor.h" #include "DemodDefs.h" #include #include #define SPECTRUM_VZM 2 #define PEAK_RESET_COUNT 30 class SpectrumVisualData { public: std::vector spectrum_points; std::vector spectrum_hold_points; double fft_ceiling, fft_floor; long long centerFreq; int bandwidth; virtual ~SpectrumVisualData() {}; }; typedef std::shared_ptr SpectrumVisualDataPtr; typedef ThreadBlockingQueue SpectrumVisualDataQueue; typedef std::shared_ptr SpectrumVisualDataQueuePtr; class SpectrumVisualProcessor : public VisualProcessor { public: SpectrumVisualProcessor(); ~SpectrumVisualProcessor(); bool isView(); void setView(bool bView); void setView(bool bView, long long centerFreq_in, long bandwidth_in); void setFFTAverageRate(float fftAverageRate); float getFFTAverageRate(); void setCenterFrequency(long long centerFreq_in); long long getCenterFrequency(); void setBandwidth(long bandwidth_in); long getBandwidth(); void setPeakHold(bool peakHold_in); bool getPeakHold(); int getDesiredInputSize(); void setup(unsigned int fftSize); void setFFTSize(unsigned int fftSize); unsigned int getFFTSize(); void setHideDC(bool hideDC); void setScaleFactor(float sf); float getScaleFactor(); protected: virtual void process(); ReBuffer outputBuffers; private: //protects all access to fields below std::mutex busy_run; bool is_view; size_t fftSize, newFFTSize; size_t fftSizeInternal; long long centerFreq; size_t bandwidth; long lastInputBandwidth; long lastBandwidth; bool lastView; liquid_float_complex *fftInput, *fftOutput, *fftInData, *fftLastData; fftplan fftPlan; unsigned int lastDataSize; double fft_ceil_ma, fft_ceil_maa; double fft_floor_ma, fft_floor_maa; double fft_ceil_peak, fft_floor_peak; float fft_average_rate; std::vector fft_result; std::vector fft_result_ma; std::vector fft_result_maa; std::vector fft_result_peak; std::vector fft_result_temp; msresamp_crcf resampler; double resamplerRatio; nco_crcf freqShifter; long shiftFrequency; std::vector shiftBuffer; std::vector resampleBuffer; size_t desiredInputSize; bool hideDC, peakHold; int peakReset; float scaleFactor; bool fftSizeChanged; }; CubicSDR-0.2.3/src/process/VisualProcessor.cpp000066400000000000000000000001461322677621400212410ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "VisualProcessor.h" CubicSDR-0.2.3/src/process/VisualProcessor.h000066400000000000000000000157611322677621400207170ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "CubicSDRDefs.h" #include "ThreadBlockingQueue.h" #include "IOThread.h" #include #include #include template class VisualProcessor { public: // typedef typename std::shared_ptr InputDataTypePtr; typedef typename std::shared_ptr OutputDataTypePtr; typedef ThreadBlockingQueue VisualInputQueueType; typedef ThreadBlockingQueue VisualOutputQueueType; typedef std::shared_ptr VisualInputQueueTypePtr; typedef std::shared_ptr VisualOutputQueueTypePtr; virtual ~VisualProcessor() { } bool isInputEmpty() { std::lock_guard < std::mutex > busy_lock(busy_update); if (input) { return input->empty(); } return true; } bool isOutputEmpty() { std::lock_guard < std::mutex > busy_lock(busy_update); for (VisualOutputQueueTypePtr single_output : outputs) { if (single_output->full()) { return false; } } return true; } bool isAnyOutputEmpty() { std::lock_guard < std::mutex > busy_lock(busy_update); for (VisualOutputQueueTypePtr single_output : outputs) { if (!(single_output)->full()) { return true; } } return false; } //Set a (new) 'input' queue for incoming data. void setInput(VisualInputQueueTypePtr vis_in) { std::lock_guard < std::mutex > busy_lock(busy_update); input = vis_in; } //Add a vis_out queue where to consumed 'input' data will be //dispatched by distribute(). void attachOutput(VisualOutputQueueTypePtr vis_out) { // attach an output queue std::lock_guard < std::mutex > busy_lock(busy_update); outputs.push_back(vis_out); } //reverse of attachOutput(), removed an existing attached vis_out. void removeOutput(VisualOutputQueueTypePtr vis_out) { // remove an output queue std::lock_guard < std::mutex > busy_lock(busy_update); auto it = std::find(outputs.begin(), outputs.end(), vis_out); if (it != outputs.end()) { outputs.erase(it); } } //Flush all queues, either input or outputs clearing their accumulated messages. //this is purposefully (almost) non-blocking call. void flushQueues() { //capture a local copy atomically, so we don't need to protect input. VisualInputQueueTypePtr localInput = input; if (localInput) { localInput->flush(); } //scoped-lock: create a local copy of outputs, and work with it. std::vector local_outputs; { std::lock_guard < std::mutex > busy_lock(busy_update); local_outputs = outputs; } for (auto single_output : local_outputs) { single_output->flush(); } } //Call process() repeateadly until all available 'input' data is consumed. void run() { //capture a local copy atomically, so we don't need to protect input. VisualInputQueueTypePtr localInput = input; if (localInput && !localInput->empty()) { process(); } } protected: // derived class must implement a process() interface //where typically 'input' data is consummed, processed, and then dispatched //with distribute() to all 'outputs'. virtual void process() = 0; //To be used by derived classes implementing //process() : will dispatch 'item' into as many //available outputs, previously set by attachOutput(). //* \param[in] timeout The number of microseconds to wait to push an item in each one of the outputs, 0(default) means indefinite wait. //* \param[in] errorMessage an error message written on std::cout in case pf push timeout. void distribute(OutputDataTypePtr item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT, const char* errorMessage = nullptr) { std::lock_guard < std::mutex > busy_lock(busy_update); //We will try to distribute 'output' among all 'outputs', //so 'output' will a-priori be shared among all 'outputs'. for (VisualOutputQueueTypePtr single_output : outputs) { //'output' can fail to be given to an single_output, //using a blocking push, with a timeout if (!(single_output)->push(item, timeout, errorMessage)) { //trace will be std::output if timeout != 0 is set and errorMessage != null. } } } //the incoming data queue VisualInputQueueTypePtr input; //the n-outputs where to process()-ed data is distribute()-ed. std::vector outputs; //protects input and outputs std::mutex busy_update; }; //Specialization much like VisualDataReDistributor, except //the input (pointer) is directly re-dispatched //to outputs, so that all output indeed SHARE the same instance. template class VisualDataDistributor : public VisualProcessor { protected: virtual void process() { typename VisualProcessor::OutputDataTypePtr inp; while (VisualProcessor::input->try_pop(inp)) { //do not try to distribute if all outputs are already full. if (!VisualProcessor::isAnyOutputEmpty()) { return; } if (inp) { VisualProcessor::distribute(inp); //inp is now shared through the distribute() call. } } } }; //specialization class which process() take an input item and re-dispatch //A COPY to every outputs, without further processing. This is a 1-to-n dispatcher. template class VisualDataReDistributor : public VisualProcessor { protected: VisualDataReDistributor() : buffers (std::string(typeid(*this).name())) { } ReBuffer buffers; virtual void process() { typename VisualProcessor::OutputDataTypePtr inp; while (VisualProcessor::input->try_pop(inp)) { //do not try to distribute if all outputs are already full. if (!VisualProcessor::isAnyOutputEmpty()) { return; } if (inp) { typename VisualProcessor::OutputDataTypePtr outp = buffers.getBuffer(); //'deep copy' of the contents (*outp) = (*inp); VisualProcessor::distribute(outp); } } } }; CubicSDR-0.2.3/src/rig/000077500000000000000000000000001322677621400144745ustar00rootroot00000000000000CubicSDR-0.2.3/src/rig/RigThread.cpp000066400000000000000000000125251322677621400170560ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "RigThread.h" std::vector RigThread::rigCaps; RigThread::RigThread() { freq = wxGetApp().getFrequency(); newFreq = freq; freqChanged.store(true); termStatus = 0; controlMode.store(true); followMode.store(true); centerLock.store(false); followModem.store(false); } RigThread::~RigThread() { } RigList &RigThread::enumerate() { if (RigThread::rigCaps.empty()) { rig_set_debug(RIG_DEBUG_ERR); rig_load_all_backends(); rig_list_foreach(RigThread::add_hamlib_rig, 0); std::sort(RigThread::rigCaps.begin(), RigThread::rigCaps.end(), rigGreater()); std::cout << "Loaded " << RigThread::rigCaps.size() << " rig models via hamlib." << std::endl; } return RigThread::rigCaps; } int RigThread::add_hamlib_rig(const struct rig_caps *rc, void* f) { rigCaps.push_back(rc); return 1; } void RigThread::initRig(rig_model_t rig_model, std::string rig_file, int serial_rate) { rigModel = rig_model; rigFile = rig_file; serialRate = serial_rate; }; void RigThread::run() { int retcode, status; termStatus = 0; std::cout << "Rig thread starting." << std::endl; rig = rig_init(rigModel); strncpy(rig->state.rigport.pathname, rigFile.c_str(), FILPATHLEN - 1); rig->state.rigport.parm.serial.rate = serialRate; retcode = rig_open(rig); if (retcode != 0) { std::cout << "Rig failed to init. " << std::endl; IOThread::terminate(); return; } char *info_buf = (char *)rig_get_info(rig); if (info_buf) { std::cout << "Rig info: " << info_buf << std::endl; } else { std::cout << "Rig info was NULL." << std::endl; } while (!stopping) { std::this_thread::sleep_for(std::chrono::milliseconds(150)); auto activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); auto lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (freqChanged.load() && (controlMode.load() || setOneShot.load())) { status = rig_get_freq(rig, RIG_VFO_CURR, &freq); if (status == 0 && !stopping) { if (freq != newFreq && setOneShot.load()) { freq = newFreq; rig_set_freq(rig, RIG_VFO_CURR, freq); // std::cout << "Set Rig Freq: %f" << newFreq << std::endl; } freqChanged.store(false); setOneShot.store(false); } else { termStatus = 0; break; } } else { freq_t checkFreq; status = rig_get_freq(rig, RIG_VFO_CURR, &checkFreq); if (status == 0 && !stopping) { if (checkFreq != freq && followMode.load()) { freq = checkFreq; if (followModem.load()) { if (lastDemod) { lastDemod->setFrequency(freq); lastDemod->updateLabel(freq); lastDemod->setFollow(true); } } else { wxGetApp().setFrequency((long long)checkFreq); } } else if (wxGetApp().getFrequency() != freq && controlMode.load() && !centerLock.load() && !followModem.load()) { freq = wxGetApp().getFrequency(); rig_set_freq(rig, RIG_VFO_CURR, freq); } else if (followModem.load()) { if (lastDemod) { if (lastDemod->getFrequency() != freq) { lastDemod->setFrequency(freq); lastDemod->updateLabel(freq); lastDemod->setFollow(true); } } } } else { termStatus = 0; break; } } if (!centerLock.load() && followModem.load() && wxGetApp().getFrequency() != freq && (lastDemod && lastDemod != activeDemod)) { wxGetApp().setFrequency((long long)freq); } // std::cout << "Rig Freq: " << freq << std::endl; } rig_close(rig); rig_cleanup(rig); std::cout << "Rig thread exiting status " << termStatus << "." << std::endl; }; freq_t RigThread::getFrequency() { if (freqChanged.load() && (setOneShot.load() || controlMode.load())) { return newFreq; } else { return freq; } } void RigThread::setFrequency(freq_t new_freq, bool oneShot) { newFreq = new_freq; freqChanged.store(true); setOneShot.store(oneShot); } void RigThread::setControlMode(bool cMode) { controlMode.store(cMode); } bool RigThread::getControlMode() { return controlMode.load(); } void RigThread::setFollowMode(bool fMode) { followMode.store(fMode); } bool RigThread::getFollowMode() { return followMode.load(); } void RigThread::setCenterLock(bool cLock) { centerLock.store(cLock); } bool RigThread::getCenterLock() { return centerLock.load(); } void RigThread::setFollowModem(bool mFollow) { followModem.store(mFollow); } bool RigThread::getFollowModem() { return followModem.load(); } CubicSDR-0.2.3/src/rig/RigThread.h000066400000000000000000000030221322677621400165130ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "IOThread.h" #include "CubicSDR.h" #include #include struct rigGreater { bool operator()( const struct rig_caps *lx, const struct rig_caps *rx ) const { std::string ln(std::string(std::string(lx->mfg_name) + " " + std::string(lx->model_name))); std::string rn(std::string(std::string(rx->mfg_name) + " " + std::string(rx->model_name))); return ln.compare(rn)<0; } }; typedef std::vector RigList; class RigThread : public IOThread { public: RigThread(); ~RigThread(); void initRig(rig_model_t rig_model, std::string rig_file, int serial_rate); virtual void run(); int terminationStatus(); freq_t getFrequency(); void setFrequency(freq_t new_freq, bool oneShot); void setControlMode(bool cMode); bool getControlMode(); void setFollowMode(bool fMode); bool getFollowMode(); void setCenterLock(bool cLock); bool getCenterLock(); void setFollowModem(bool mFollow); bool getFollowModem(); static RigList &enumerate(); static int add_hamlib_rig(const struct rig_caps *rc, void* f); private: RIG *rig; rig_model_t rigModel; std::string rigFile; int serialRate; int termStatus; freq_t freq; freq_t newFreq; std::atomic_bool freqChanged, setOneShot; std::atomic_bool controlMode, followMode, centerLock, followModem; static RigList rigCaps; };CubicSDR-0.2.3/src/sdr/000077500000000000000000000000001322677621400145035ustar00rootroot00000000000000CubicSDR-0.2.3/src/sdr/SDRDeviceInfo.cpp000066400000000000000000000155151322677621400176020ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SDRDeviceInfo.h" #include "CubicSDRDefs.h" #include #include SDRDeviceInfo::SDRDeviceInfo() : name(""), serial(""), available(false), remote(false), manual(false), soapyDevice(nullptr) { active.store(false); } SDRDeviceInfo::~SDRDeviceInfo() { if (soapyDevice != nullptr) { SoapySDR::Device::unmake(soapyDevice); } } std::string SDRDeviceInfo::getDeviceId() { std::string deviceId; deviceId.append(getName()); // deviceId.append(" :: "); // deviceId.append(getSerial()); return deviceId; } int SDRDeviceInfo::getIndex() const { return index; } void SDRDeviceInfo::setIndex(const int index) { this->index = index; } bool SDRDeviceInfo::isAvailable() const { return available; } void SDRDeviceInfo::setAvailable(bool available) { this->available = available; } bool SDRDeviceInfo::isActive() const { return active.load(); } void SDRDeviceInfo::setActive(bool active) { this->active.store(active); } const std::string& SDRDeviceInfo::getName() const { return name; } void SDRDeviceInfo::setName(const std::string& name) { this->name = name; } const std::string& SDRDeviceInfo::getSerial() const { return serial; } void SDRDeviceInfo::setSerial(const std::string& serial) { this->serial = serial; } const std::string& SDRDeviceInfo::getTuner() const { return tuner; } void SDRDeviceInfo::setTuner(const std::string& tuner) { this->tuner = tuner; } const std::string& SDRDeviceInfo::getManufacturer() const { return manufacturer; } void SDRDeviceInfo::setManufacturer(const std::string& manufacturer) { this->manufacturer = manufacturer; } const std::string& SDRDeviceInfo::getProduct() const { return product; } void SDRDeviceInfo::setProduct(const std::string& product) { this->product = product; } const std::string& SDRDeviceInfo::getDriver() const { return driver; } void SDRDeviceInfo::setDriver(const std::string& driver) { this->driver = driver; } const std::string& SDRDeviceInfo::getHardware() const { return hardware; } void SDRDeviceInfo::setHardware(const std::string& hardware) { this->hardware = hardware; } bool SDRDeviceInfo::hasTimestamps() const { return timestamps; } void SDRDeviceInfo::setTimestamps(bool timestamps) { this->timestamps = timestamps; } bool SDRDeviceInfo::isRemote() const { return remote; } void SDRDeviceInfo::setRemote(bool remote) { this->remote = remote; } bool SDRDeviceInfo::isManual() const { return manual; } void SDRDeviceInfo::setManual(bool manual) { this->manual = manual; } void SDRDeviceInfo::setManualParams(std::string manualParams) { this->manual_params = manualParams; } std::string SDRDeviceInfo::getManualParams() { return manual_params; } void SDRDeviceInfo::setDeviceArgs(SoapySDR::Kwargs deviceArgs) { this->deviceArgs = deviceArgs; } SoapySDR::Kwargs SDRDeviceInfo::getDeviceArgs() { return deviceArgs; } void SDRDeviceInfo::setStreamArgs(SoapySDR::Kwargs streamArgs) { this->streamArgs = streamArgs; } SoapySDR::Kwargs SDRDeviceInfo::getStreamArgs() { return streamArgs; } void SDRDeviceInfo::setSoapyDevice(SoapySDR::Device *dev) { if (soapyDevice) { SoapySDR::Device::unmake(soapyDevice); } soapyDevice = dev; } SoapySDR::Device *SDRDeviceInfo::getSoapyDevice() { if (soapyDevice == nullptr) { soapyDevice = SoapySDR::Device::make(deviceArgs); } return soapyDevice; } bool SDRDeviceInfo::hasCORR(int direction, size_t channel) { SoapySDR::Device *dev = getSoapyDevice(); std::vector freqs = dev->listFrequencies(direction, channel); if (std::find(freqs.begin(), freqs.end(), "CORR") != freqs.end()) { return true; } else { return false; } } std::vector SDRDeviceInfo::getSampleRates(int direction, size_t channel) { SoapySDR::Device *dev = getSoapyDevice(); size_t nbMaxDifferentRates = DEVICE_SAMPLE_RATES_MAX_NB; std::vector result; //the original list returned from the driver: std::vector sampleRates = dev->listSampleRates(direction, channel); //be paranoid, sort by increasing rates... std::sort(sampleRates.begin(), sampleRates.end(), [](double a, double b) -> bool { return a < b; }); //if sampleRates.size() > nbMaxDifferentRates, decimate this number to only return nbMaxDifferentRates sample //rates values. size_t sampleRateSelectionStep = 1; if (sampleRates.size() / nbMaxDifferentRates >= 2) { sampleRateSelectionStep = sampleRates.size() / nbMaxDifferentRates; } for (size_t i = 0; sampleRateSelectionStep * i < sampleRates.size(); i++) { //convert to longs... result.push_back((long)sampleRates[sampleRateSelectionStep * i]); } //always include the biggest value: if ((long)sampleRates.back() > result.back()) { result.push_back((long)sampleRates.back()); } return result; } std::vector SDRDeviceInfo::getAntennaNames(int direction, size_t channel) { SoapySDR::Device *dev = getSoapyDevice(); if (dev) { return dev->listAntennas(direction, channel); } return std::vector(); } std::string SDRDeviceInfo::getAntennaName(int direction, size_t channel) { SoapySDR::Device *dev = getSoapyDevice(); if (dev) { return dev->getAntenna(direction, channel); } return std::string(""); } long SDRDeviceInfo::getSampleRateNear(int direction, size_t channel, long sampleRate_in) { std::vector sampleRates = getSampleRates(direction, channel); long returnRate = sampleRates[0]; long sDelta = (long)sampleRate_in-sampleRates[0]; long minDelta = std::abs(sDelta); for (long i : sampleRates) { long thisDelta = std::abs(sampleRate_in - i); if (thisDelta < minDelta) { minDelta = thisDelta; returnRate = i; } } return returnRate; } SDRRangeMap SDRDeviceInfo::getGains(int direction, size_t channel) { SoapySDR::Device *dev = getSoapyDevice(); std::vector gainNames = dev->listGains(direction, channel); std::map gainMap; for (std::string gname : gainNames) { gainMap[gname] = dev->getGainRange(direction, channel, gname); } return gainMap; } //read the current gain of name gainName (must exit in getGains(), else return 0) //in the device. double SDRDeviceInfo::getCurrentGain(int direction, size_t channel, const std::string& gainName) { SoapySDR::Device *dev = getSoapyDevice(); if (dev) { std::vector gainNames = dev->listGains(direction, channel); auto itFoundName = std::find(gainNames.begin(), gainNames.end(), gainName); if (itFoundName != gainNames.end()) { return dev->getGain(direction, channel, gainName); } } return 0.0; } CubicSDR-0.2.3/src/sdr/SDRDeviceInfo.h000066400000000000000000000056461322677621400172530ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include typedef struct _SDRManualDef { std::string factory; std::string params; } SDRManualDef; typedef std::map SDRRangeMap; class SDRDeviceInfo { public: SDRDeviceInfo(); ~SDRDeviceInfo(); std::string getDeviceId(); int getIndex() const; void setIndex(const int index); bool isAvailable() const; void setAvailable(bool available); bool isActive() const; void setActive(bool active); const std::string& getName() const; void setName(const std::string& name); const std::string& getSerial() const; void setSerial(const std::string& serial); const std::string& getTuner() const; void setTuner(const std::string& tuner); const std::string& getManufacturer() const; void setManufacturer(const std::string& manufacturer); const std::string& getProduct() const; void setProduct(const std::string& product); const std::string& getDriver() const; void setDriver(const std::string& driver); const std::string& getHardware() const; void setHardware(const std::string& hardware); bool hasTimestamps() const; void setTimestamps(bool timestamps); bool isRemote() const; void setRemote(bool remote); bool isManual() const; void setManual(bool manual); void setManualParams(std::string manualParams); std::string getManualParams(); void setDeviceArgs(SoapySDR::Kwargs deviceArgs); SoapySDR::Kwargs getDeviceArgs(); void setStreamArgs(SoapySDR::Kwargs deviceArgs); SoapySDR::Kwargs getStreamArgs(); // void setSettingsInfo(SoapySDR::ArgInfoList settingsArgs); // SoapySDR::ArgInfoList getSettingsArgInfo(); // std::vector getSettingNames(); void setSoapyDevice(SoapySDR::Device *dev); SoapySDR::Device *getSoapyDevice(); bool hasCORR(int direction, size_t channel); std::vector getSampleRates(int direction, size_t channel); std::vector getAntennaNames(int direction, size_t channel); std::string getAntennaName(int direction, size_t channel); long getSampleRateNear(int direction, size_t channel, long sampleRate_in); SDRRangeMap getGains(int direction, size_t channel); //read the current gain of name gainName (must exist in getGains(), else return 0) //in the device. double getCurrentGain(int direction, size_t channel, const std::string& gainName); private: int index = 0; std::string name, serial, product, manufacturer, tuner; std::string driver, hardware, manual_params; bool timestamps, available, remote, manual; std::atomic_bool active; SoapySDR::Kwargs deviceArgs, streamArgs; SoapySDR::Device *soapyDevice; }; CubicSDR-0.2.3/src/sdr/SDREnumerator.cpp000066400000000000000000000360301322677621400177030ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SDREnumerator.h" #include "CubicSDRDefs.h" #include #include "CubicSDR.h" #include #ifdef WIN32 #include #endif std::vector SDREnumerator::factories; std::vector SDREnumerator::modules; std::vector SDREnumerator::remotes; std::map< std::string, std::vector > SDREnumerator::devs; bool SDREnumerator::soapy_initialized = false; bool SDREnumerator::has_remote = false; std::vector SDREnumerator::manuals; SDREnumerator::SDREnumerator() : IOThread() { } SDREnumerator::~SDREnumerator() { } // Some utility from SoapySDR :) static std::string trim(const std::string &s) { #if WIN32 std::string out = s; locale loc(""); while (not out.empty() and std::isspace(out[0], loc)) out = out.substr(1); while (not out.empty() and std::isspace(out[out.size() - 1], loc)) out = out.substr(0, out.size() - 1); return out; #else std::string out = s; while (not out.empty() and std::isspace(out[0])) out = out.substr(1); while (not out.empty() and std::isspace(out[out.size()-1])) out = out.substr(0, out.size()-1); return out; #endif } SoapySDR::Kwargs SDREnumerator::argsStrToKwargs(const std::string &args) { SoapySDR::Kwargs kwargs; bool inKey = true; std::string key, val; for (size_t i = 0; i < args.size(); i++) { const char ch = args[i]; if (inKey) { if (ch == '=') inKey = false; else if (ch == ',') inKey = true; else key += ch; } else { if (ch == ',') inKey = true; else val += ch; } if ((inKey and not val.empty()) or ((i+1) == args.size())) { key = trim(key); val = trim(val); if (not key.empty()) kwargs[key] = val; key = ""; val = ""; } } return kwargs; } std::vector *SDREnumerator::enumerate_devices(std::string remoteAddr, bool noInit) { if (SDREnumerator::devs[remoteAddr].size()) { return &SDREnumerator::devs[remoteAddr]; } if (noInit) { return NULL; } if (!soapy_initialized) { std::cout << "SoapySDR init.." << std::endl << std::flush; std::cout << "\tAPI Version: v" << SoapySDR::getAPIVersion() << std::endl << std::flush; std::cout << "\tABI Version: v" << SoapySDR::getABIVersion() << std::endl << std::flush; std::cout << "\tInstall root: " << SoapySDR::getRootPath() << std::endl << std::flush; std::cout << "\tLoading modules... " << std::endl << std::flush; std::string userModPath = wxGetApp().getModulePath(); if (userModPath != "") { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules from " + userModPath + ".."); std::vector localMods = SoapySDR::listModules(userModPath); for (std::string mod : localMods) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing user specified SoapySDR module " + (mod) + ".."); std::cout << "Initializing user specified SoapySDR module " << (mod) << ".." << std::endl << std::flush; SoapySDR::loadModule(mod); } } else { #ifdef BUNDLE_SOAPY_MODS #ifdef BUNDLED_MODS_ONLY wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath()); std::vector localMods = SoapySDR::listModules(exePath.GetPath().ToStdString() + "/modules/"); for (std::vector::iterator mods_i = localMods.begin(); mods_i != localMods.end(); mods_i++) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing bundled SoapySDR module " + (*mods_i) + ".."); std::cout << "Loading bundled SoapySDR module " << (*mods_i) << ".." << std::endl << std::flush; SoapySDR::loadModule(*mods_i); } #else bool localModPref = wxGetApp().getUseLocalMod(); if (localModPref) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules.."); std::cout << "Checking local system SoapySDR modules.." << std::endl << std::flush; SoapySDR::loadModules(); } wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath()); std::vector localMods = SoapySDR::listModules(exePath.GetPath().ToStdString() + "/modules/"); for (std::string mod : localMods) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing bundled SoapySDR module " + (mod) + ".."); std::cout << "Loading bundled SoapySDR module " << (mod) << ".." << std::endl << std::flush; SoapySDR::loadModule(mod); } if (!localModPref) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules.."); std::cout << "Checking system SoapySDR modules.." << std::endl << std::flush; SoapySDR::loadModules(); } #endif #else wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules.."); SoapySDR::loadModules(); #endif } SDREnumerator::factories.clear(); std::cout << "\tAvailable factories..."; SoapySDR::FindFunctions factories = SoapySDR::Registry::listFindFunctions(); for (SoapySDR::FindFunctions::const_iterator it = factories.begin(); it != factories.end(); ++it) { if (it != factories.begin()) { std::cout << ", "; } std::cout << it->first; if (it->first == "remote") { has_remote = true; } SDREnumerator::factories.push_back(it->first); } if (factories.empty()) { std::cout << "No factories found!" << std::endl; } if ((factories.size() == 1) && factories.find("null") != factories.end()) { std::cout << "Just 'null' factory found." << std::endl; wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_FAILED, std::string("No modules available.")); } std::cout << std::endl; soapy_initialized = true; } modules = SoapySDR::listModules(); std::vector results; SoapySDR::Kwargs enumArgs; bool isRemote = false; if (remoteAddr.length()) { std::cout << "Enumerating remote address: " << remoteAddr << std::endl; enumArgs["driver"] = "remote"; enumArgs["remote"] = remoteAddr; isRemote = true; results = SoapySDR::Device::enumerate(enumArgs); } else { results = SoapySDR::Device::enumerate(); } size_t manualsIdx = results.size(); std::vector manualParams; std::vector manualResult; if (manuals.size()) { for (std::vector::const_iterator m_i = manuals.begin(); m_i != manuals.end(); m_i++) { std::vector manual_result; std::string strDevArgs = "driver="+m_i->factory+","+m_i->params; manualParams.push_back(m_i->params); wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Enumerating manual device '") + strDevArgs + "'.."); manual_result = SoapySDR::Device::enumerate(strDevArgs); if (manual_result.size()) { for (std::vector::const_iterator i = manual_result.begin(); i != manual_result.end(); i++) { results.push_back(*i); manualResult.push_back(true); } } else { SoapySDR::Kwargs failedEnum; failedEnum = argsStrToKwargs(strDevArgs); failedEnum["label"] = "Not Found ("+m_i->factory+")"; results.push_back(failedEnum); manualResult.push_back(false); } } } if (isRemote) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Opening remote server ") + remoteAddr + ".."); } for (size_t i = 0; i < results.size(); i++) { SDRDeviceInfo *dev = new SDRDeviceInfo(); SoapySDR::Kwargs deviceArgs = results[i]; for (SoapySDR::Kwargs::const_iterator it = deviceArgs.begin(); it != deviceArgs.end(); ++it) { std::cout << " " << it->first << " = " << it->second << std::endl; if (it->first == "driver") { dev->setDriver(it->second); } else if (it->first == "label" || it->first == "device") { dev->setName(it->second); } } if (deviceArgs.count("remote")) { isRemote = true; } else { isRemote = false; } dev->setRemote(isRemote); dev->setManual(i>=manualsIdx); if (i>=manualsIdx) { dev->setManualParams(manualParams[i-manualsIdx]); } std::cout << "Make device " << i << std::endl; if (igetHardwareInfo(); for (SoapySDR::Kwargs::const_iterator it = info.begin(); it != info.end(); ++it) { std::cout << " " << it->first << "=" << it->second << std::endl; if (it->first == "hardware") { dev->setHardware(it->second); } } if (isRemote) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Querying remote " + remoteAddr + " device #" + std::to_string(i) + ": " + dev-> getName()); } else { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Querying device #") + std::to_string(i) + ": " + dev->getName()); } SoapySDR::ArgInfoList settingsInfo = device->getSettingInfo(); DeviceConfig *cfg = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); ConfigSettings devSettings = cfg->getSettings(); if (devSettings.size()) { // Load the saved device settings to deviceArgs, and back to settingsInfo. for (ConfigSettings::const_iterator set_i = devSettings.begin(); set_i != devSettings.end(); set_i++) { deviceArgs[set_i->first] = set_i->second; } for (size_t j = 0; j < settingsInfo.size(); j++) { if (deviceArgs.find(settingsInfo[j].key) != deviceArgs.end()) { settingsInfo[j].value = deviceArgs[settingsInfo[j].key]; } } } dev->setDeviceArgs(deviceArgs); SoapySDR::Device::unmake(device); dev->setAvailable(true); } catch (const std::exception &ex) { std::cerr << "Error making device: " << ex.what() << std::endl; wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Error querying device #") + std::to_string(i)); dev->setAvailable(false); } else { dev->setAvailable(false); } std::cout << std::endl; SDREnumerator::devs[remoteAddr].push_back(dev); } if (SDREnumerator::devs[remoteAddr].empty()) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("No devices found!")); } std::cout << std::endl; return &SDREnumerator::devs[remoteAddr]; } void SDREnumerator::run() { std::cout << "SDR enumerator starting." << std::endl; wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Scanning local devices, please wait.."); SDREnumerator::enumerate_devices(""); if (remotes.size()) { for (std::string remote : remotes) { wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Scanning devices at " + (remote) + ", please wait.."); SDREnumerator::enumerate_devices(remote); } } std::cout << "Reporting enumeration complete." << std::endl; wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_DEVICES_READY, "Finished scanning devices."); std::cout << "SDR enumerator done." << std::endl; } void SDREnumerator::addRemote(std::string remoteAddr) { std::vector::iterator remote_i = std::find(remotes.begin(), remotes.end(), remoteAddr); if (remote_i != remotes.end()) { return; } else { remotes.push_back(remoteAddr); } } void SDREnumerator::removeRemote(std::string remoteAddr) { std::vector::iterator remote_i = std::find(remotes.begin(), remotes.end(), remoteAddr); if (remote_i != remotes.end()) { if (devs.find(*remote_i) != devs.end()) { while (devs[*remote_i].size()) { SDRDeviceInfo *devRemove = devs[*remote_i].back(); devs[*remote_i].pop_back(); delete devRemove; } } remotes.erase(remote_i); } else { return; } } std::vector &SDREnumerator::getRemotes() { return remotes; } void SDREnumerator::addManual(std::string factory, std::string params) { SDRManualDef def; def.factory = factory; def.params = params; manuals.push_back(def); } void SDREnumerator::removeManual(std::string factory, std::string params) { for (std::vector::iterator i = manuals.begin(); i != manuals.end(); i++) { if (i->factory == factory && i->params == params) { manuals.erase(i); for (std::vector::iterator subdevs_i = devs[""].begin(); subdevs_i != devs[""].end(); subdevs_i++) { if ((*subdevs_i)->isManual() && (*subdevs_i)->getDriver() == factory && (*subdevs_i)->getManualParams() == params) { devs[""].erase(subdevs_i); break; } } break; } } } std::vector &SDREnumerator::getManuals() { return SDREnumerator::manuals; } void SDREnumerator::setManuals(std::vector manuals) { SDREnumerator::manuals = manuals; } bool SDREnumerator::hasRemoteModule() { return SDREnumerator::has_remote; } void SDREnumerator::reset() { soapy_initialized = false; factories.clear(); modules.clear(); for (std::map< std::string, std::vector >::iterator di = devs.begin(); di != devs.end(); di++) { for (std::vector::iterator i = di->second.begin(); i != di->second.end(); i++) { (*i)->setSoapyDevice(nullptr); } } devs.clear(); } std::vector &SDREnumerator::getFactories() { return SDREnumerator::factories; } CubicSDR-0.2.3/src/sdr/SDREnumerator.h000066400000000000000000000031401322677621400173440ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include "IOThread.h" #include "SDRDeviceInfo.h" #include "AppConfig.h" #include #include #include #include class SDREnumerator: public IOThread { private: public: SDREnumerator(); ~SDREnumerator(); enum SDREnumState { SDR_ENUM_DEVICES_READY, SDR_ENUM_MESSAGE, SDR_ENUM_TERMINATED, SDR_ENUM_FAILED }; static std::vector *enumerate_devices(std::string remoteAddr = "", bool noInit=false); virtual void run(); static SoapySDR::Kwargs argsStrToKwargs(const std::string &args); static void addRemote(std::string remoteAddr); static void removeRemote(std::string remoteAddr); static std::vector &getRemotes(); static bool hasRemoteModule(); static void addManual(std::string factory, std::string params); static void removeManual(std::string factory, std::string params); static std::vector &getManuals(); static void setManuals(std::vector manuals); static void reset(); static std::vector &getFactories(); protected: static bool soapy_initialized, has_remote; static std::vector factories; static std::vector modules; static std::vector remotes; static std::map< std::string, std::vector > devs; static std::vector manuals; static std::mutex devs_busy; }; CubicSDR-0.2.3/src/sdr/SDRPostThread.cpp000066400000000000000000000352241322677621400176430ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SDRPostThread.h" #include "CubicSDRDefs.h" #include "CubicSDR.h" #include #include #include //50 ms #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000) SDRPostThread::SDRPostThread() : IOThread(), buffers("SDRPostThreadBuffers"), visualDataBuffers("SDRPostThreadVisualDataBuffers"), frequency(0) { iqDataInQueue = nullptr; iqDataOutQueue = nullptr; iqVisualQueue = nullptr; numChannels = 0; channelizer = nullptr; sampleRate = 0; visFrequency.store(0); visBandwidth.store(0); doRefresh.store(false); dcFilter = iirfilt_crcf_create_dc_blocker(0.0005f); } SDRPostThread::~SDRPostThread() { } void SDRPostThread::notifyDemodulatorsChanged() { doRefresh.store(true); } void SDRPostThread::initPFBChannelizer() { // std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl; if (channelizer) { firpfbch_crcf_destroy(channelizer); } channelizer = firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 4, 60); chanBw = (sampleRate / numChannels); chanCenters.resize(numChannels+1); demodChannelActive.resize(numChannels+1); // std::cout << "Channel bandwidth spacing: " << (chanBw) << std::endl; } void SDRPostThread::updateActiveDemodulators() { // In range? runDemods.clear(); demodChannel.clear(); long long centerFreq = wxGetApp().getFrequency(); //retreive the current list of demodulators: auto demodulators = wxGetApp().getDemodMgr().getDemodulators(); for (auto demod : demodulators) { // not in range? if (demod->isDeltaLock()) { if (demod->getFrequency() != centerFreq + demod->getDeltaLockOfs()) { demod->setFrequency(centerFreq + demod->getDeltaLockOfs()); demod->updateLabel(demod->getFrequency()); demod->setFollow(false); demod->setTracking(false); } } if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { // deactivate if active if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == demod) { demod->setActive(false); } else if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) { demod->setActive(false); } // follow if follow mode if (demod->isFollow() && centerFreq != demod->getFrequency()) { wxGetApp().setFrequency(demod->getFrequency()); demod->setFollow(false); } } else if (!demod->isActive()) { // in range, activate if not activated demod->setActive(true); if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == nullptr) { wxGetApp().getDemodMgr().setActiveDemodulator(demod); } } if (!demod->isActive()) { continue; } // Add active demods to the current run: runDemods.push_back(demod); demodChannel.push_back(-1); } } void SDRPostThread::resetAllDemodulators() { //retreive the current list of demodulators: auto demodulators = wxGetApp().getDemodMgr().getDemodulators(); for (auto demod : demodulators) { demod->setActive(false); demod->getIQInputDataPipe()->flush(); } doRefresh = true; } void SDRPostThread::updateChannels() { // calculate channel center frequencies, todo: cache for (int i = 0; i < numChannels/2; i++) { int ofs = ((chanBw) * i); chanCenters[i] = frequency + ofs; chanCenters[i+(numChannels/2)] = frequency - (sampleRate/2) + ofs; } chanCenters[numChannels] = frequency + (sampleRate/2); } int SDRPostThread::getChannelAt(long long frequency) { int chan = -1; long long minDelta = sampleRate; for (int i = 0; i < numChannels+1; i++) { long long fdelta = abs(frequency - chanCenters[i]); if (fdelta < minDelta) { minDelta = fdelta; chan = i; } } return chan; } void SDRPostThread::setIQVisualRange(long long frequency, int bandwidth) { visFrequency.store(frequency); visBandwidth.store(bandwidth); } void SDRPostThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread int priority = sched_get_priority_max( SCHED_FIFO); sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif // std::cout << "SDR post-processing thread started.." << std::endl; iqDataInQueue = std::static_pointer_cast(getInputQueue("IQDataInput")); iqDataOutQueue = std::static_pointer_cast(getOutputQueue("IQDataOutput")); iqVisualQueue = std::static_pointer_cast(getOutputQueue("IQVisualDataOutput")); iqActiveDemodVisualQueue = std::static_pointer_cast(getOutputQueue("IQActiveDemodVisualDataOutput")); while (!stopping) { SDRThreadIQDataPtr data_in; if (!iqDataInQueue->pop(data_in, HEARTBEAT_CHECK_PERIOD_MICROS)) { continue; } bool doUpdate = false; if (data_in && data_in->data.size()) { if(data_in->numChannels > 1) { runPFBCH(data_in.get()); } else { runSingleCH(data_in.get()); } } for (size_t j = 0; j < runDemods.size(); j++) { DemodulatorInstancePtr demod = runDemods[j]; if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { doUpdate = true; } } //Only update the list of demodulators here if (doUpdate || doRefresh) { updateActiveDemodulators(); } } //end while //Be safe, remove as many elements as possible iqVisualQueue->flush(); iqDataInQueue->flush(); iqDataOutQueue->flush(); iqActiveDemodVisualQueue->flush(); // std::cout << "SDR post-processing thread done." << std::endl; } void SDRPostThread::terminate() { IOThread::terminate(); //unblock push() iqVisualQueue->flush(); iqDataInQueue->flush(); iqDataOutQueue->flush(); iqActiveDemodVisualQueue->flush(); } void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) { if (sampleRate != data_in->sampleRate) { sampleRate = data_in->sampleRate; numChannels = 1; doRefresh.store(true); } size_t dataSize = data_in->data.size(); size_t outSize = data_in->data.size(); if (outSize > dataOut.capacity()) { dataOut.reserve(outSize); } if (outSize != dataOut.size()) { dataOut.resize(outSize); } if (frequency != data_in->frequency) { frequency = data_in->frequency; doRefresh.store(true); } if (doRefresh.load()) { updateActiveDemodulators(); doRefresh.store(false); } size_t refCount = runDemods.size(); bool doIQDataOut = (iqDataOutQueue != nullptr && !iqDataOutQueue->full()); bool doDemodVisOut = (runDemods.size() > 0 && iqActiveDemodVisualQueue != nullptr && !iqActiveDemodVisualQueue->full()); bool doVisOut = (iqVisualQueue != nullptr && !iqVisualQueue->full()); if (doIQDataOut) { refCount++; } if (doDemodVisOut) { refCount++; } if (doVisOut) { refCount++; } if (refCount) { DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer(); demodDataOut->frequency = frequency; demodDataOut->sampleRate = sampleRate; if (demodDataOut->data.size() != dataSize) { if (demodDataOut->data.capacity() < dataSize) { demodDataOut->data.reserve(dataSize); } demodDataOut->data.resize(dataSize); } iirfilt_crcf_execute_block(dcFilter, &data_in->data[0], dataSize, &demodDataOut->data[0]); if (doDemodVisOut) { //VSO: blocking push iqActiveDemodVisualQueue->push(demodDataOut); } if (doIQDataOut) { //VSO: blocking push iqDataOutQueue->push(demodDataOut); } if (doVisOut) { //VSO: blocking push iqVisualQueue->push(demodDataOut); } for (size_t i = 0; i < runDemods.size(); i++) { // try-push() : we do our best to only stimulate active demods, but some could happen to be dead, full, or indeed non-active. //so in short never block here no matter what. if (!runDemods[i]->getIQInputDataPipe()->try_push(demodDataOut)) { // std::cout << "SDRPostThread::runSingleCH() attempt to push into demod '" << runDemods[i]->getLabel() // << "' (" << runDemods[i]->getFrequency() << " Hz) failed, demod is either too busy, not-active, or dead..." << std::endl << std::flush; std::this_thread::yield(); } } } } void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) { if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) { numChannels = data_in->numChannels; sampleRate = data_in->sampleRate; initPFBChannelizer(); doRefresh.store(true); } size_t dataSize = data_in->data.size(); size_t outSize = data_in->data.size(); if (outSize > dataOut.capacity()) { dataOut.reserve(outSize); } if (outSize != dataOut.size()) { dataOut.resize(outSize); } if (iqDataOutQueue != nullptr && !iqDataOutQueue->full()) { DemodulatorThreadIQDataPtr iqDataOut = visualDataBuffers.getBuffer(); bool doVis = false; if (iqVisualQueue != nullptr && !iqVisualQueue->full()) { doVis = true; } iqDataOut->frequency = data_in->frequency; iqDataOut->sampleRate = data_in->sampleRate; iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize); //VSO: blocking push iqDataOutQueue->push(iqDataOut); if (doVis) { //VSO: blocking push iqVisualQueue->push(iqDataOut); } } if (frequency != data_in->frequency) { frequency = data_in->frequency; doRefresh.store(true); } if (doRefresh.load()) { updateActiveDemodulators(); updateChannels(); doRefresh.store(false); } DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); int activeDemodChannel = -1; // Find active demodulators if (runDemods.size() > 0) { // channelize data // firpfbch output rate is (input rate / channels) for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) { firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]); } for (int i = 0, iMax = numChannels+1; i < iMax; i++) { demodChannelActive[i] = 0; } // Find nearest channel for each demodulator for (size_t i = 0; i < runDemods.size(); i++) { DemodulatorInstancePtr demod = runDemods[i]; demodChannel[i] = getChannelAt(demod->getFrequency()); if (demod == activeDemod) { activeDemodChannel = demodChannel[i]; } } for (size_t i = 0; i < runDemods.size(); i++) { // cache channel usage refcounts if (demodChannel[i] >= 0) { demodChannelActive[demodChannel[i]]++; } } // Run channels for (int i = 0; i < numChannels+1; i++) { int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != nullptr) && !iqActiveDemodVisualQueue->full())?1:0; if (!doDemodVis && demodChannelActive[i] == 0) { continue; } DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer(); demodDataOut->frequency = chanCenters[i]; demodDataOut->sampleRate = chanBw; // Calculate channel buffer size size_t chanDataSize = (outSize/numChannels); if (demodDataOut->data.size() != chanDataSize) { if (demodDataOut->data.capacity() < chanDataSize) { demodDataOut->data.reserve(chanDataSize); } demodDataOut->data.resize(chanDataSize); } int idx = i; // Extra channel wraps lower side band of lowest channel // to fix frequency gap on upper side of spectrum if (i == numChannels) { idx = (numChannels/2); } // prepare channel data buffer if (i == 0) { // Channel 0 requires DC correction if (dcBuf.size() != chanDataSize) { dcBuf.resize(chanDataSize); } for (size_t j = 0; j < chanDataSize; j++) { dcBuf[j] = dataOut[idx]; idx += numChannels; } iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]); } else { for (size_t j = 0; j < chanDataSize; j++) { demodDataOut->data[j] = dataOut[idx]; idx += numChannels; } } if (doDemodVis) { //VSO: blocking push iqActiveDemodVisualQueue->push(demodDataOut); } for (size_t j = 0; j < runDemods.size(); j++) { if (demodChannel[j] == i) { // try-push() : we do our best to only stimulate active demods, but some could happen to be dead, full, or indeed non-active. //so in short never block here no matter what. if (!runDemods[j]->getIQInputDataPipe()->try_push(demodDataOut)) { // std::cout << "SDRPostThread::runPFBCH() attempt to push into demod '" << runDemods[i]->getLabel() // << "' (" << runDemods[i]->getFrequency() << " Hz) failed, demod is either too busy, not-active, or dead..." << std::endl << std::flush; std::this_thread::yield(); } } } //end for } } } CubicSDR-0.2.3/src/sdr/SDRPostThread.h000066400000000000000000000030021322677621400172750ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "SoapySDRThread.h" #include class SDRPostThread : public IOThread { public: SDRPostThread(); ~SDRPostThread(); void notifyDemodulatorsChanged(); virtual void run(); virtual void terminate(); void runSingleCH(SDRThreadIQData *data_in); void runPFBCH(SDRThreadIQData *data_in); void setIQVisualRange(long long frequency, int bandwidth); protected: SDRThreadIQDataQueuePtr iqDataInQueue; DemodulatorThreadInputQueuePtr iqDataOutQueue; DemodulatorThreadInputQueuePtr iqVisualQueue; DemodulatorThreadInputQueuePtr iqActiveDemodVisualQueue; private: void initPFBChannelizer(); void updateActiveDemodulators(); void updateChannels(); int getChannelAt(long long frequency); void resetAllDemodulators(); ReBuffer buffers; std::vector fpData; std::vector dataOut; std::vector chanCenters; long long chanBw = 0; std::vector runDemods; std::vector demodChannel; std::vector demodChannelActive; ReBuffer visualDataBuffers; atomic_bool doRefresh; atomic_llong visFrequency; atomic_int visBandwidth; int numChannels, sampleRate; long long frequency; firpfbch_crcf channelizer; iirfilt_crcf dcFilter; std::vector dcBuf; }; CubicSDR-0.2.3/src/sdr/SoapySDRThread.cpp000066400000000000000000000610101322677621400200010ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SoapySDRThread.h" #include "CubicSDRDefs.h" #include #include "CubicSDR.h" #include #include #include #include #define TARGET_DISPLAY_FPS 60 SDRThread::SDRThread() : IOThread(), buffers("SDRThreadBuffers") { device = nullptr; deviceConfig.store(nullptr); deviceInfo.store(nullptr); sampleRate.store(DEFAULT_SAMPLE_RATE); frequency.store(0); offset.store(0); ppm.store(0); numElems.store(0); rate_changed.store(false); freq_changed.store(false); offset_changed.store(false); antenna_changed.store(false); ppm_changed .store(false); device_changed.store(false); hasPPM.store(false); hasHardwareDC.store(false); numChannels.store(8); agc_mode.store(true); agc_mode_changed.store(false); gain_value_changed.store(false); setting_value_changed.store(false); frequency_lock_init.store(false); frequency_locked.store(false); lock_freq.store(0); iq_swap.store(false); } SDRThread::~SDRThread() { } SoapySDR::Kwargs SDRThread::combineArgs(SoapySDR::Kwargs a, SoapySDR::Kwargs b) { SoapySDR::Kwargs c; SoapySDR::Kwargs::iterator i; for (i = a.begin(); i != a.end(); i++) { c[i->first] = i->second; } for (i = b.begin(); i != b.end(); i++) { c[i->first] = i->second; } return c; } bool SDRThread::init() { //#warning Debug On // SoapySDR_setLogLevel(SOAPY_SDR_DEBUG); SDRDeviceInfo *devInfo = deviceInfo.load(); deviceConfig.store(wxGetApp().getConfig()->getDevice(devInfo->getDeviceId())); DeviceConfig *devConfig = deviceConfig.load(); ppm.store(devConfig->getPPM()); ppm_changed.store(true); std::string driverName = devInfo->getDriver(); offset = devConfig->getOffset(); SoapySDR::Kwargs args = devInfo->getDeviceArgs(); wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Initializing device.")); device = devInfo->getSoapyDevice(); SoapySDR::Kwargs currentStreamArgs = combineArgs(devInfo->getStreamArgs(),streamArgs); std::string streamExceptionStr(""); try { stream = device->setupStream(SOAPY_SDR_RX,"CF32", std::vector(), currentStreamArgs); } catch(exception e) { streamExceptionStr = e.what(); } if (!stream) { wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_FAILED, std::string("Stream setup failed, stream is null. ") + streamExceptionStr); std::cout << "Stream setup failed, stream is null. " << streamExceptionStr << std::endl; return false; } int streamMTU = device->getStreamMTU(stream); mtuElems.store(streamMTU); std::cout << "Device Stream MTU: " << mtuElems.load() << std::endl << std::flush; deviceInfo.load()->setStreamArgs(currentStreamArgs); deviceConfig.load()->setStreamOpts(currentStreamArgs); wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Activating stream.")); device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load()); // TODO: explore bandwidth setting option to see if this is necessary for others if (device->getDriverKey() == "bladeRF") { device->setBandwidth(SOAPY_SDR_RX, 0, sampleRate.load()); } device->setFrequency(SOAPY_SDR_RX,0,"RF",frequency - offset.load()); device->activateStream(stream); if (devInfo->hasCORR(SOAPY_SDR_RX, 0)) { hasPPM.store(true); device->setFrequency(SOAPY_SDR_RX,0,"CORR",ppm.load()); } else { hasPPM.store(false); } if (device->hasDCOffsetMode(SOAPY_SDR_RX, 0)) { hasHardwareDC.store(true); // wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Found hardware DC offset correction support, internal disabled.")); device->setDCOffsetMode(SOAPY_SDR_RX, 0, true); } else { hasHardwareDC.store(false); } device->setGainMode(SOAPY_SDR_RX,0,agc_mode.load()); numChannels.store(getOptimalChannelCount(sampleRate.load())); numElems.store(getOptimalElementCount(sampleRate.load(), TARGET_DISPLAY_FPS)); //fallback if mtuElems was wrong. if (!mtuElems.load()) { mtuElems.store(numElems.load()); } overflowBuffer.data.resize(mtuElems.load()); buffs[0] = malloc(mtuElems.load() * 4 * sizeof(float)); numOverflow = 0; SoapySDR::ArgInfoList settingsInfo = device->getSettingInfo(); SoapySDR::ArgInfoList::const_iterator settings_i; if (!setting_value_changed.load()) { settings.erase(settings.begin(), settings.end()); settingChanged.erase(settingChanged.begin(), settingChanged.end()); } //apply settings. { //enter scoped-lock std::lock_guard < std::mutex > lock(setting_busy); for (settings_i = settingsInfo.begin(); settings_i != settingsInfo.end(); settings_i++) { SoapySDR::ArgInfo setting = (*settings_i); if ((settingChanged.find(setting.key) != settingChanged.end()) && (settings.find(setting.key) != settings.end())) { device->writeSetting(setting.key, settings[setting.key]); settingChanged[setting.key] = false; } else { settings[setting.key] = device->readSetting(setting.key); settingChanged[setting.key] = false; } } setting_value_changed.store(false); } //leave lock guard scope updateSettings(); wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_INITIALIZED, std::string("Device Initialized.")); //rebuild menu now that settings are really been applied. wxGetApp().notifyMainUIOfDeviceChange(true); return true; } void SDRThread::deinit() { device->deactivateStream(stream); device->closeStream(stream); free(buffs[0]); } void SDRThread::assureBufferMinSize(SDRThreadIQData * dataOut, size_t minSize) { if (dataOut->data.size() < minSize) { dataOut->data.resize(minSize); } } //Called in an infinite loop, read SaopySDR device to build // a 'this.numElems' sized batch of samples (SDRThreadIQData) and push it into iqDataOutQueue. //this batch of samples is built to represent 1 frame / TARGET_DISPLAY_FPS. int SDRThread::readStream(SDRThreadIQDataQueuePtr iqDataOutQueue) { int flags; long long timeNs; int n_read = 0; int nElems = numElems.load(); int mtElems = mtuElems.load(); // Warning: if MTU > numElems, i.e if device MTU is too big w.r.t the sample rate, the TARGET_DISPLAY_FPS cannot //be reached and the CubicSDR displays "slows down". //To get back a TARGET_DISPLAY_FPS, the user need to adapt //the SoapySDR Device to use smaller buffer sizes, because // readStream() is suited to device MTU and cannot be really adapted dynamically. //TODO: Add in doc the need to reduce SoapySDR device buffer length (if available) to restore higher fps. //0. Retreive a new batch SDRThreadIQDataPtr dataOut = buffers.getBuffer(); //resize to the target size immedialetly, to minimize later reallocs: assureBufferMinSize(dataOut.get(), nElems); //1.If overflow occured on the previous readStream(), transfer it in dataOut directly. if (numOverflow > 0) { int n_overflow = std::min(numOverflow, nElems); //safety assureBufferMinSize(dataOut.get(), n_overflow); ::memcpy(&dataOut->data[0], &overflowBuffer.data[0], n_overflow * sizeof(liquid_float_complex)); n_read = n_overflow; //is still > 0 if MTU > nElements (low sample rate w.r.t the MTU !) numOverflow -= n_overflow; // std::cout << "SDRThread::readStream() 1.1 overflowBuffer not empty, collect the remaining " << n_overflow << " samples in it..." << std::endl; if (numOverflow > 0) { // still some left, shift the remaining samples to the begining.. ::memmove(&overflowBuffer.data[0], &overflowBuffer.data[n_overflow], numOverflow * sizeof(liquid_float_complex)); // std::cout << "SDRThread::readStream() 1.2 overflowBuffer still not empty, compact the remaining " << numOverflow << " samples in it..." << std::endl; } } //end if numOverflow > 0 int readStreamCode = 0; //2. attempt readStream() at most nElems, by mtElems-sized chunks, append in dataOut->data directly. while (n_read < nElems && !stopping) { //Whatever the number of remaining samples needed to reach nElems, we always try to read a mtElems-size chunk, //from which SoapySDR effectively returns n_stream_read. int n_stream_read = device->readStream(stream, buffs, mtElems, flags, timeNs); readStreamCode = n_stream_read; //if the n_stream_read <= 0, bail out from reading. if (n_stream_read == 0) { std::cout << "SDRThread::readStream(): 2. SoapySDR read blocking..." << std::endl; break; } else if (n_stream_read < 0) { std::cout << "SDRThread::readStream(): 2. SoapySDR read failed with code: " << n_stream_read << std::endl; break; } //sucess read beyond nElems, so with overflow: if ((n_read + n_stream_read) > nElems) { //n_requested is the exact number to reach nElems. int n_requested = nElems-n_read; //Copy at most n_requested CF32 into .data liquid_float_complex, //starting at n_read position. //inspired from SoapyRTLSDR code, this mysterious void** is indeed an array of CF32(real/imag) samples, indeed an array of //float with the following layout [sample 1 real part , sample 1 imag part, sample 2 real part , sample 2 imag part,sample 3 real part , sample 3 imag part,...etc] //Since there is indeed no garantee that sizeof(liquid_float_complex) = 2 * sizeof (float) //nor that the Re/Im layout of fields matches the float array order, assign liquid_float_complex field by field. float *pp = (float *)buffs[0]; //safety assureBufferMinSize(dataOut.get(), n_read + n_requested); if (iq_swap.load()) { for (int i = 0; i < n_requested; i++) { dataOut->data[n_read + i].imag = pp[2 * i]; dataOut->data[n_read + i].real = pp[2 * i + 1]; } } else { for (int i = 0; i < n_requested; i++) { dataOut->data[n_read + i].real = pp[2 * i]; dataOut->data[n_read + i].imag = pp[2 * i + 1]; } } //shift of n_requested samples, each one made of 2 floats... pp += n_requested * 2; //numNewOverflow are in exess, they have to be added in the existing overflowBuffer. int numNewOverflow = n_stream_read - n_requested; //so push the remainder samples to overflowBuffer: if (numNewOverflow > 0) { // std::cout << "SDRThread::readStream(): 2. SoapySDR read make nElems overflow by " << numNewOverflow << " samples..." << std::endl; } //safety assureBufferMinSize(&overflowBuffer, numOverflow + numNewOverflow); if (iq_swap.load()) { for (int i = 0; i < numNewOverflow; i++) { overflowBuffer.data[numOverflow + i].imag = pp[2 * i]; overflowBuffer.data[numOverflow + i].real = pp[2 * i + 1]; } } else { for (int i = 0; i < numNewOverflow; i++) { overflowBuffer.data[numOverflow + i].real = pp[2 * i]; overflowBuffer.data[numOverflow + i].imag = pp[2 * i + 1]; } } numOverflow += numNewOverflow; n_read += n_requested; } else if (n_stream_read > 0) { // no overflow, read the whole n_stream_read. float *pp = (float *)buffs[0]; //safety assureBufferMinSize(dataOut.get(), n_read + n_stream_read); if (iq_swap.load()) { for (int i = 0; i < n_stream_read; i++) { dataOut->data[n_read + i].imag = pp[2 * i]; dataOut->data[n_read + i].real = pp[2 * i + 1]; } } else { for (int i = 0; i < n_stream_read; i++) { dataOut->data[n_read + i].real = pp[2 * i]; dataOut->data[n_read + i].imag = pp[2 * i + 1]; } } n_read += n_stream_read; } else { break; } } //end while //3. At that point, dataOut contains nElems (or less if a read has return an error), try to post in queue, else discard. if (n_read > 0 && !stopping && !iqDataOutQueue->full()) { //clamp result to the actual read size: dataOut->data.resize(n_read); dataOut->frequency = frequency.load(); dataOut->sampleRate = sampleRate.load(); dataOut->dcCorrected = hasHardwareDC.load(); dataOut->numChannels = numChannels.load(); if (!iqDataOutQueue->try_push(dataOut)) { //The rest of the system saturates, //finally the push didn't suceeded. readStreamCode = -32; std::cout << "SDRThread::readStream(): 3.2 iqDataOutQueue output queue is full, discard processing of the batch..." << std::endl; //saturation, let a chance to the other threads to consume the existing samples std::this_thread::yield(); } } else { readStreamCode = -31; std::cout << "SDRThread::readStream(): 3.1 iqDataOutQueue output queue is full, discard processing of the batch..." << std::endl; //saturation, let a chance to the other threads to consume the existing samples std::this_thread::yield(); } return readStreamCode; } void SDRThread::readLoop() { SDRThreadIQDataQueuePtr iqDataOutQueue = std::static_pointer_cast( getOutputQueue("IQDataOutput")); if (iqDataOutQueue == nullptr) { return; } updateGains(); while (!stopping.load()) { updateSettings(); readStream(iqDataOutQueue); } //End while iqDataOutQueue->flush(); } void SDRThread::updateGains() { SDRDeviceInfo *devInfo = deviceInfo.load(); gainValues.erase(gainValues.begin(),gainValues.end()); gainChanged.erase(gainChanged.begin(),gainChanged.end()); SDRRangeMap gains = devInfo->getGains(SOAPY_SDR_RX, 0); for (SDRRangeMap::iterator gi = gains.begin(); gi != gains.end(); gi++) { gainValues[gi->first] = device->getGain(SOAPY_SDR_RX, 0, gi->first); gainChanged[gi->first] = false; } gain_value_changed.store(false); } void SDRThread::updateSettings() { bool doUpdate = false; if (!stream) { return; } if (antenna_changed.load()) { device->setAntenna(SOAPY_SDR_RX, 0, antennaName); antenna_changed.store(false); } if (offset_changed.load()) { if (!freq_changed.load()) { frequency.store(frequency.load()); freq_changed.store(true); } offset_changed.store(false); } if (rate_changed.load()) { device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load()); // TODO: explore bandwidth setting option to see if this is necessary for others if (device->getDriverKey() == "bladeRF") { device->setBandwidth(SOAPY_SDR_RX, 0, sampleRate.load()); } sampleRate.store(device->getSampleRate(SOAPY_SDR_RX,0)); numChannels.store(getOptimalChannelCount(sampleRate.load())); numElems.store(getOptimalElementCount(sampleRate.load(), TARGET_DISPLAY_FPS)); int streamMTU = device->getStreamMTU(stream); mtuElems.store(streamMTU); //fallback if mtuElems was wrong if (!mtuElems.load()) { mtuElems.store(numElems.load()); } overflowBuffer.data.resize(mtuElems.load()); free(buffs[0]); buffs[0] = malloc(mtuElems.load() * 4 * sizeof(float)); //clear overflow buffer numOverflow = 0; rate_changed.store(false); doUpdate = true; } if (ppm_changed.load() && hasPPM.load()) { device->setFrequency(SOAPY_SDR_RX,0,"CORR",ppm.load()); ppm_changed.store(false); } if (freq_changed.load()) { if (frequency_locked.load() && !frequency_lock_init.load()) { device->setFrequency(SOAPY_SDR_RX,0,"RF",lock_freq.load()); frequency_lock_init.store(true); } else if (!frequency_locked.load()) { device->setFrequency(SOAPY_SDR_RX,0,"RF",frequency.load() - offset.load()); } freq_changed.store(false); } // double devFreq = device->getFrequency(SOAPY_SDR_RX,0); // if (((long long)devFreq + offset.load()) != frequency.load()) { // wxGetApp().setFrequency((long long)devFreq + offset.load()); // } if (agc_mode_changed.load()) { device->setGainMode(SOAPY_SDR_RX, 0, agc_mode.load()); agc_mode_changed.store(false); if (!agc_mode.load()) { updateGains(); //re-apply the saved configuration gains: DeviceConfig *devConfig = deviceConfig.load(); ConfigGains gains = devConfig->getGains(); for (ConfigGains::iterator gain_i = gains.begin(); gain_i != gains.end(); gain_i++) { setGain(gain_i->first, gain_i->second); } } doUpdate = true; } if (gain_value_changed.load() && !agc_mode.load()) { std::lock_guard < std::mutex > lock(gain_busy); for (std::map::iterator gci = gainChanged.begin(); gci != gainChanged.end(); gci++) { if (gci->second) { device->setGain(SOAPY_SDR_RX, 0, gci->first, gainValues[gci->first]); gainChanged[gci->first] = false; } } gain_value_changed.store(false); } if (setting_value_changed.load()) { std::lock_guard < std::mutex > lock(setting_busy); for (std::map::iterator sci = settingChanged.begin(); sci != settingChanged.end(); sci++) { if (sci->second) { device->writeSetting(sci->first, settings[sci->first]); settingChanged[sci->first] = false; } } setting_value_changed.store(false); doUpdate = true; } if (doUpdate) { wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_INITIALIZED, std::string("Settings updated.")); } } void SDRThread::run() { //#ifdef __APPLE__ // pthread_t tID = pthread_self(); // ID of this thread // int priority = sched_get_priority_max( SCHED_FIFO); // sched_param prio = { priority }; // scheduling priority of thread // pthread_setschedparam(tID, SCHED_FIFO, &prio); //#endif std::cout << "SDR thread starting." << std::endl; SDRDeviceInfo *activeDev = deviceInfo.load(); if (activeDev != nullptr) { std::cout << "device init()" << std::endl; if (!init()) { std::cout << "SDR Thread stream init error." << std::endl; return; } std::cout << "starting readLoop()" << std::endl; activeDev->setActive(true); readLoop(); activeDev->setActive(false); std::cout << "readLoop() ended." << std::endl; deinit(); std::cout << "device deinit()" << std::endl; } else { std::cout << "SDR Thread started with null device?" << std::endl; } std::cout << "SDR thread done." << std::endl; } void SDRThread::terminate() { IOThread::terminate(); SDRThreadIQDataQueuePtr iqDataOutQueue = std::static_pointer_cast(getOutputQueue("IQDataOutput")); if (iqDataOutQueue != nullptr) { iqDataOutQueue->flush(); } } SDRDeviceInfo *SDRThread::getDevice() { return deviceInfo.load(); } void SDRThread::setDevice(SDRDeviceInfo *dev) { deviceInfo.store(dev); if (dev) { deviceConfig.store(wxGetApp().getConfig()->getDevice(dev->getDeviceId())); } else { deviceConfig.store(nullptr); } } int SDRThread::getOptimalElementCount(long long sampleRate, int fps) { int elemCount = (int)floor((double)sampleRate/(double)fps); int nch = numChannels.load(); elemCount = int(ceil((double)elemCount/(double)nch))*nch; // std::cout << "Calculated optimal " << numChannels.load() << " channel element count of " << elemCount << std::endl; return elemCount; } int SDRThread::getOptimalChannelCount(long long sampleRate) { if (sampleRate <= CHANNELIZER_RATE_MAX) { return 1; } int optimal_rate = CHANNELIZER_RATE_MAX; int optimal_count = int(ceil(double(sampleRate)/double(optimal_rate))); if (optimal_count % 2 == 1) { optimal_count--; } if (optimal_count < 2) { optimal_count = 2; } return optimal_count; } void SDRThread::setFrequency(long long freq) { if (freq < sampleRate.load() / 2) { freq = sampleRate.load() / 2; } frequency.store(freq); freq_changed.store(true); } long long SDRThread::getFrequency() { return frequency.load(); } void SDRThread::lockFrequency(long long freq) { lock_freq.store(freq); frequency_locked.store(true); frequency_lock_init.store(false); setFrequency(freq); } bool SDRThread::isFrequencyLocked() { return frequency_locked.load(); } void SDRThread::unlockFrequency() { frequency_locked.store(false); frequency_lock_init.store(false); freq_changed.store(true); } void SDRThread::setOffset(long long ofs) { offset.store(ofs); offset_changed.store(true); DeviceConfig *devConfig = deviceConfig.load(); if (devConfig) { devConfig->setOffset(ofs); } // std::cout << "Set offset: " << offset.load() << std::endl; } long long SDRThread::getOffset() { return offset.load(); } void SDRThread::setAntenna(const std::string& name) { antennaName = name; antenna_changed.store(true); DeviceConfig *devConfig = deviceConfig.load(); if (devConfig) { devConfig->setAntennaName(antennaName); } } std::string SDRThread::getAntenna() { return antennaName; } void SDRThread::setSampleRate(long rate) { sampleRate.store(rate); rate_changed = true; DeviceConfig *devConfig = deviceConfig.load(); if (devConfig) { devConfig->setSampleRate(rate); } // std::cout << "Set sample rate: " << sampleRate.load() << std::endl; } long SDRThread::getSampleRate() { return sampleRate.load(); } void SDRThread::setPPM(int ppm) { this->ppm.store(ppm); ppm_changed.store(true); DeviceConfig *devConfig = deviceConfig.load(); if (devConfig) { devConfig->setPPM(ppm); } // std::cout << "Set PPM: " << this->ppm.load() << std::endl; } int SDRThread::getPPM() { return ppm.load(); } void SDRThread::setAGCMode(bool mode) { agc_mode.store(mode); agc_mode_changed.store(true); DeviceConfig *devConfig = deviceConfig.load(); if (devConfig) { devConfig->setAGCMode(mode); } } bool SDRThread::getAGCMode() { return agc_mode.load(); } void SDRThread::setIQSwap(bool swap) { iq_swap.store(swap); } bool SDRThread::getIQSwap() { return iq_swap.load(); } void SDRThread::setGain(std::string name, float value) { std::lock_guard < std::mutex > lock(gain_busy); gainValues[name] = value; gainChanged[name] = true; gain_value_changed.store(true); DeviceConfig *devConfig = deviceConfig.load(); if (devConfig) { devConfig->setGain(name, value); } } float SDRThread::getGain(std::string name) { std::lock_guard < std::mutex > lock(gain_busy); float val = gainValues[name]; return val; } void SDRThread::writeSetting(std::string name, std::string value) { std::lock_guard < std::mutex > lock(setting_busy); settings[name] = value; settingChanged[name] = true; setting_value_changed.store(true); if (deviceConfig.load() != nullptr) { deviceConfig.load()->setSetting(name, value); } } std::string SDRThread::readSetting(std::string name) { std::string val; std::lock_guard < std::mutex > lock(setting_busy); val = device->readSetting(name); return val; } void SDRThread::setStreamArgs(SoapySDR::Kwargs streamArgs_in) { streamArgs = streamArgs_in; } CubicSDR-0.2.3/src/sdr/SoapySDRThread.h000066400000000000000000000073471322677621400174630ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include "ThreadBlockingQueue.h" #include "DemodulatorMgr.h" #include "SDRDeviceInfo.h" #include "AppConfig.h" #include #include #include #include #include class SDRThreadIQData { public: long long frequency; long long sampleRate; bool dcCorrected; int numChannels; std::vector data; SDRThreadIQData() : frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), dcCorrected(true), numChannels(0) { } SDRThreadIQData(long long bandwidth, long long frequency, std::vector * /* data */) : frequency(frequency), sampleRate(bandwidth) { } virtual ~SDRThreadIQData() { } }; typedef std::shared_ptr SDRThreadIQDataPtr; typedef ThreadBlockingQueue SDRThreadIQDataQueue; typedef std::shared_ptr SDRThreadIQDataQueuePtr; class SDRThread : public IOThread { private: bool init(); void deinit(); //returns the SoapyDevice readStream return value, //i.e if >= 0 the numbre of samples read, else if < 0 an error code. int readStream(SDRThreadIQDataQueuePtr iqDataOutQueue); void readLoop(); public: SDRThread(); ~SDRThread(); enum SDRThreadState { SDR_THREAD_MESSAGE, SDR_THREAD_INITIALIZED, SDR_THREAD_FAILED}; virtual void run(); virtual void terminate(); SDRDeviceInfo *getDevice(); void setDevice(SDRDeviceInfo *dev); int getOptimalElementCount(long long sampleRate, int fps); int getOptimalChannelCount(long long sampleRate); void setFrequency(long long freq); long long getFrequency(); void lockFrequency(long long freq); bool isFrequencyLocked(); void unlockFrequency(); void setOffset(long long ofs); long long getOffset(); void setAntenna(const std::string& name); std::string getAntenna(); void setSampleRate(long rate); long getSampleRate(); void setPPM(int ppm); int getPPM(); void setAGCMode(bool mode); bool getAGCMode(); void setIQSwap(bool swap); bool getIQSwap(); void setGain(std::string name, float value); float getGain(std::string name); void writeSetting(std::string name, std::string value); std::string readSetting(std::string name); void setStreamArgs(SoapySDR::Kwargs streamArgs); protected: void updateGains(); void updateSettings(); SoapySDR::Kwargs combineArgs(SoapySDR::Kwargs a, SoapySDR::Kwargs b); SoapySDR::Stream *stream; SoapySDR::Device *device; void *buffs[1]; ReBuffer buffers; SDRThreadIQData overflowBuffer; int numOverflow; std::atomic deviceConfig; std::atomic deviceInfo; std::mutex setting_busy; std::map settings; std::map settingChanged; std::atomic_llong sampleRate; std::atomic_llong frequency, offset, lock_freq; std::atomic_int ppm, numElems, mtuElems, numChannels; std::atomic_bool hasPPM, hasHardwareDC; std::string antennaName; std::atomic_bool agc_mode, rate_changed, freq_changed, offset_changed, antenna_changed, ppm_changed, device_changed, agc_mode_changed, gain_value_changed, setting_value_changed, frequency_locked, frequency_lock_init, iq_swap; std::mutex gain_busy; std::map gainValues; std::map gainChanged; SoapySDR::Kwargs streamArgs; private: void assureBufferMinSize(SDRThreadIQData * dataOut, size_t minSize); }; CubicSDR-0.2.3/src/ui/000077500000000000000000000000001322677621400143305ustar00rootroot00000000000000CubicSDR-0.2.3/src/ui/GLPanel.cpp000066400000000000000000000274551322677621400163330ustar00rootroot00000000000000 // Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "GLPanel.h" #include "cubic_math.h" #include using namespace CubicVR; GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), visible(true), transform(mat4::identity()) { pos[0] = 0.0f; pos[1] = 0.0f; rot[0] = 0.0f; rot[1] = 0.0f; rot[2] = 0.0f; size[0] = 1.0f; size[1] = 1.0f; fill[0] = RGBA4f(0.5f,0.5f,0.5f); fill[1] = RGBA4f(0.1f,0.1f,0.1f); borderColor = RGBA4f(0.8f, 0.8f, 0.8f); setCoordinateSystem(GLPANEL_Y_UP); setMarginPx(0); setBorderPx(0); srcBlend = GL_SRC_ALPHA; dstBlend = GL_ONE_MINUS_SRC_ALPHA; } void GLPanel::genArrays() { float min = -1.0, mid = 0, max = 1.0; if (fillType == GLPANEL_FILL_SOLID || fillType == GLPANEL_FILL_GRAD_X || fillType == GLPANEL_FILL_GRAD_Y) { glPoints.reserve(2 * 4); glPoints.resize(2 * 4); glColors.reserve(4 * 4); glColors.resize(4 * 4); float pts[2 * 4] = { min, min, min, max, max, max, max, min }; RGBA4f c[4]; if (fillType == GLPANEL_FILL_SOLID) { c[0] = c[1] = c[2] = c[3] = fill[0]; } else if (fillType == GLPANEL_FILL_GRAD_X) { c[0] = c[1] = fill[0]; c[2] = c[3] = fill[1]; } else if (fillType == GLPANEL_FILL_GRAD_Y) { c[0] = c[3] = fill[0]; c[1] = c[2] = fill[1]; } float clr[4 * 4] = { c[0].r, c[0].g, c[0].b, c[0].a, c[1].r, c[1].g, c[1].b, c[1].a, c[2].r, c[2].g, c[2].b, c[2].a, c[3].r, c[3].g, c[3].b, c[3].a }; glPoints.assign(pts, pts + (2 * 4)); glColors.assign(clr, clr + (4 * 4)); } else { glPoints.reserve(2 * 8); glPoints.resize(2 * 8); glColors.reserve(4 * 8); glColors.resize(4 * 8); RGBA4f c[8]; if (fillType == GLPANEL_FILL_GRAD_BAR_X) { float pts[2 * 8] = { min, min, min, max, mid, max, mid, min, mid, min, mid, max, max, max, max, min }; glPoints.assign(pts, pts + (2 * 8)); c[0] = c[1] = fill[0]; c[2] = c[3] = fill[1]; c[4] = c[5] = fill[1]; c[6] = c[7] = fill[0]; } else if (fillType == GLPANEL_FILL_GRAD_BAR_Y) { float pts[2 * 8] = { min, min, min, mid, max, mid, max, min, min, mid, min, max, max, max, max, mid }; glPoints.assign(pts, pts + (2 * 8)); c[0] = c[3] = fill[0]; c[1] = c[2] = fill[1]; c[4] = c[7] = fill[1]; c[5] = c[6] = fill[0]; } float clr[4 * 8] = { c[0].r, c[0].g, c[0].b, c[0].a, c[1].r, c[1].g, c[1].b, c[1].a, c[2].r, c[2].g, c[2].b, c[2].a, c[3].r, c[3].g, c[3].b, c[3].a, c[4].r, c[4].g, c[4].b, c[4].a, c[5].r, c[5].g, c[5].b, c[5].a, c[6].r, c[6].g, c[6].b, c[6].a, c[7].r, c[7].g, c[7].b, c[7].a }; glColors.assign(clr, clr + (4 * 8)); } } void GLPanel::setViewport() { GLint vp[4]; glGetIntegerv(GL_VIEWPORT, vp); view[0] = vp[2]; view[1] = vp[3]; } void GLPanel::setPosition(float x, float y) { pos[0] = x; pos[1] = y; } void GLPanel::setSize(float w, float h) { size[0] = w; size[1] = h; } float GLPanel::getWidth() { return size[0]; } float GLPanel::getHeight() { return size[1]; } float GLPanel::getWidthPx() { return pdim.x; } float GLPanel::getHeightPx() { return pdim.y; } void GLPanel::setCoordinateSystem(GLPanelCoordinateSystem coord_in) { coord = coord_in; if (coord == GLPANEL_Y_DOWN || coord == GLPANEL_Y_UP) { min = -1; mid = 0; max = 1; } else { min = 0; mid = 0.5; max = 1; } genArrays(); } bool GLPanel::hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result) { CubicVR::vec4 hitPos = CubicVR::mat4::vec4_multiply(CubicVR::vec4(pos.x, pos.y, 0.0, 1.0), transformInverse); if (hitPos.x >= -1.0 && hitPos.x <= 1.0 && hitPos.y >= -1.0 && hitPos.y <= 1.0) { result.x = hitPos.x; result.y = hitPos.y; return true; } return false; } void GLPanel::setFill(GLPanelFillType fill_mode) { fillType = fill_mode; genArrays(); } void GLPanel::setFillColor(RGBA4f color1) { fill[0] = color1; genArrays(); } void GLPanel::setFillColor(RGBA4f color1, RGBA4f color2) { fill[0] = color1; fill[1] = color2; genArrays(); } void GLPanel::setMarginPx(float marg) { marginPx = marg; } void GLPanel::setBorderColor(RGBA4f clr) { borderColor = clr; } void GLPanel::setBorderPx(float bord) { borderPx.left = borderPx.right = borderPx.top = borderPx.bottom = bord; } void GLPanel::setBorderPx(float bordl, float bordr, float bordt, float bordb) { borderPx.left = bordl; borderPx.right = bordr; borderPx.top = bordt; borderPx.bottom = bordb; } void GLPanel::setBlend(GLuint src, GLuint dst) { srcBlend = src; dstBlend = dst; } void GLPanel::addChild(GLPanel *childPanel) { std::vector::iterator i = std::find(children.begin(), children.end(), childPanel); if (i == children.end()) { children.push_back(childPanel); } } void GLPanel::removeChild(GLPanel *childPanel) { std::vector::iterator i = std::find(children.begin(), children.end(), childPanel); if (i != children.end()) { children.erase(i); } } void GLPanel::drawChildren() { if (children.size()) { std::vector::iterator panel_i; for (panel_i = children.begin(); panel_i != children.end(); panel_i++) { (*panel_i)->calcTransform(transform); (*panel_i)->draw(); } } } void GLPanel::drawPanelContents() { drawChildren(); } void GLPanel::calcTransform(mat4 transform_in) { // compute local transform localTransform = mat4::translate(pos[0], pos[1], 0) * mat4::scale(size[0], size[1], 1); if (rot[0] || rot[1] || rot[2]) { localTransform *= mat4::rotate(rot[0], rot[1], rot[2]); } // compute global transform transform = transform_in * localTransform; // init view[] setViewport(); // get min/max transform vec4 vmin_t = mat4::vec4_multiply(vec4(min, min, 0, 1), transform); vec4 vmax_t = mat4::vec4_multiply(vec4(max, max, 0, 1), transform); // screen dimensions vmin = vec2((vmin_t.x > vmax_t.x)?vmax_t.x:vmin_t.x, (vmin_t.y > vmax_t.y)?vmax_t.y:vmin_t.y); vmax = vec2((vmin_t.x > vmax_t.x)?vmin_t.x:vmax_t.x, (vmin_t.y > vmax_t.y)?vmin_t.y:vmax_t.y); // unit dimensions umin = (vmin * 0.5) + vec2(1,1); umax = (vmax * 0.5) + vec2(1,1); ucenter = vec2((umin + umax) * 0.5); // pixel dimensions pdim = vec2((vmax.x - vmin.x) / 2.0 * view[0], (vmax.y - vmin.y) / 2.0 * view[1]); pvec = vec2(((vmax.x - vmin.x) / 2.0) / pdim.x, ((vmax.y - vmin.y) / 2.0) / pdim.y); // std::cout << umin << " :: " << ucenter << " :: " << pdim << " :: " << pvec << std::endl; if (marginPx) { transform *= mat4::scale(1.0 - marginPx * 2.0 * pvec.x / size[0], 1.0 - marginPx * 2.0 * pvec.y / size[1], 1); } transformInverse = CubicVR::mat4::inverse(transform); } void GLPanel::draw() { float min = -1.0, max = 1.0; glLoadMatrixf(transform.to_ptr()); if (fillType != GLPANEL_FILL_NONE && visible) { glEnable(GL_BLEND); glBlendFunc(srcBlend, dstBlend); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &glPoints[0]); glColorPointer(4, GL_FLOAT, 0, &glColors[0]); glDrawArrays(GL_QUADS, 0, glPoints.size() / 2); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); if (borderPx.left || borderPx.right || borderPx.top || borderPx.bottom) { glEnable(GL_LINE_SMOOTH); glColor4f(borderColor.r, borderColor.g, borderColor.b, borderColor.a); if (borderPx.left) { glLineWidth(borderPx.left); glBegin(GL_LINES); glVertex2f(min, min); glVertex2f(min, max); glEnd(); } if (borderPx.right) { glLineWidth(borderPx.right); glBegin(GL_LINES); glVertex2f(max, min); glVertex2f(max, max); glEnd(); } if (borderPx.top) { glLineWidth(borderPx.top); glBegin(GL_LINES); glVertex2f(min, min); glVertex2f(max, min); glEnd(); } if (borderPx.bottom) { glLineWidth(borderPx.bottom); glBegin(GL_LINES); glVertex2f(min, max); glVertex2f(max, max); glEnd(); } glDisable(GL_LINE_SMOOTH); } glDisable(GL_BLEND); } if (contentsVisible) { mat4 mCoord = mat4::identity(); if (coord == GLPANEL_Y_DOWN_ZERO_ONE) { mCoord *= mat4::translate(-1.0f, 1.0f, 0.0f) * mat4::scale(2.0f, -2.0f, 2.0f); } if (coord == GLPANEL_Y_UP_ZERO_ONE) { mCoord = mat4::translate(-1.0f, -1.0f, 0.0f) * mat4::scale(2.0f, 2.0f, 2.0f); } if (coord == GLPANEL_Y_DOWN) { mCoord = mat4::scale(1.0f, -1.0f, 1.0f); } // if (coord == GLPANEL_Y_UP) { // } glLoadMatrixf((transform * mCoord).to_ptr()); drawPanelContents(); } } GLTextPanel::GLTextPanel() : GLPanel() { coord = GLPANEL_Y_UP; horizAlign = GLFont::GLFONT_ALIGN_CENTER; vertAlign = GLFont::GLFONT_ALIGN_CENTER; useNativeFont = true; } void GLTextPanel::drawPanelContents() { glColor4f(1, 1, 1, 1.0); float pdimy = pdim.y; double appliedScaleFactor = GLFont::getScaleFactor(); if (useNativeFont) { appliedScaleFactor = 1.0; } //pdimy is considered un-scaled pdimy = round(pdimy / appliedScaleFactor); int size = 12; if (pdimy <= 16) { size = 12; } else if (pdimy <= 18) { size = 16; } else if(pdimy <= 24) { size = 18; } else if(pdimy <= 32) { size = 24; } else if(pdimy <= 48) { size = 32; } else { size = 48; } GLFont::getFont(size, appliedScaleFactor).drawString(textVal, mid, mid, horizAlign, vertAlign, (int)pdim.x, (int)pdim.y); } void GLTextPanel::setText(std::string text, GLFont::Align hAlign, GLFont::Align vAlign, bool useNative) { textVal = text; horizAlign = hAlign; vertAlign = vAlign; useNativeFont = useNative; } std::string GLTextPanel::getText() { return textVal; } void GLTestPanel::drawPanelContents() { glColor3f(1.0,1.0,1.0); glBegin(GL_LINES); glVertex2f(min, mid); glVertex2f(max, mid); glVertex2f(mid, min); glVertex2f(mid, max); glVertex2f(mid, max); glVertex2f(mid - 0.02, max - 0.2); glVertex2f(mid, 1); glVertex2f(mid + 0.02, max - 0.2); glVertex2f(max, mid); glVertex2f(max - 0.1, mid + max * 0.25); glVertex2f(max, mid); glVertex2f(max - 0.1, mid - max * 0.25); glEnd(); } CubicSDR-0.2.3/src/ui/GLPanel.h000066400000000000000000000061371322677621400157720ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include "GLExt.h" #include "GLFont.h" #include "ColorTheme.h" #include "cubic_math.h" class GLPanelEdges { public: float left; float right; float top; float bottom; GLPanelEdges(): left(0), right(0), top(0), bottom(0) { } GLPanelEdges(float l, float r, float t, float b) { left = l; right = r; top = t; bottom = b; } }; class GLPanel { private: std::vector glPoints; std::vector glColors; void genArrays(); void setViewport(); public: typedef enum GLPanelFillType { GLPANEL_FILL_NONE, GLPANEL_FILL_SOLID, GLPANEL_FILL_GRAD_X, GLPANEL_FILL_GRAD_Y, GLPANEL_FILL_GRAD_BAR_X, GLPANEL_FILL_GRAD_BAR_Y } GLPanelFillType; typedef enum GLPanelCoordinateSystem { GLPANEL_Y_DOWN_ZERO_ONE, GLPANEL_Y_UP_ZERO_ONE, GLPANEL_Y_UP, GLPANEL_Y_DOWN } GLPanelCoordinateSystem; float pos[2] = {0.0f,0.0f}; float rot[3] = { 0.0f,0.0f,0.0f }; float size[2] = { 0.0f,0.0f }; float view[2] = { 0.0f,0.0f }; GLPanelFillType fillType; GLPanelCoordinateSystem coord; float marginPx; GLPanelEdges borderPx; RGBA4f fill[2]; RGBA4f borderColor; bool contentsVisible, visible; CubicVR::mat4 transform, transformInverse; CubicVR::mat4 localTransform; float min, mid, max; // screen dimensions CubicVR::vec2 vmin, vmax; // unit dimensions CubicVR::vec2 umin, umax, ucenter; // pixel dimensions CubicVR::vec2 pdim, pvec; GLuint srcBlend, dstBlend; std::vector children; GLPanel(); virtual ~GLPanel() {}; void setPosition(float x, float y); void setSize(float w, float h); float getWidth(); float getHeight(); float getWidthPx(); float getHeightPx(); void setCoordinateSystem(GLPanelCoordinateSystem coord); bool hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result); void setFill(GLPanelFillType fill_mode); void setFillColor(RGBA4f color1); void setFillColor(RGBA4f color1, RGBA4f color2); void setMarginPx(float marg); void setBorderColor(RGBA4f clr); void setBorderPx(float bord); void setBorderPx(float bordl, float bordr, float bordt, float bordb); void setBlend(GLuint src, GLuint dst); void addChild(GLPanel *childPanel); void removeChild(GLPanel *childPanel); void drawChildren(); virtual void drawPanelContents(); void calcTransform(CubicVR::mat4 transform); void draw(); }; class GLTextPanel : public GLPanel { private: std::string textVal; GLFont::Align horizAlign; GLFont::Align vertAlign; bool useNativeFont; public: GLTextPanel(); void drawPanelContents(); void setText(std::string text, GLFont::Align hAlign = GLFont::GLFONT_ALIGN_CENTER, GLFont::Align vAlign = GLFont::GLFONT_ALIGN_CENTER , bool useNativeFont = false); std::string getText(); }; class GLTestPanel : public GLPanel { public: GLTestPanel() : GLPanel() { } void drawPanelContents(); }; CubicSDR-0.2.3/src/ui/UITestCanvas.cpp000066400000000000000000000040121322677621400173420ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "UITestCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include wxBEGIN_EVENT_TABLE(UITestCanvas, wxGLCanvas) EVT_PAINT(UITestCanvas::OnPaint) EVT_IDLE(UITestCanvas::OnIdle) EVT_MOTION(UITestCanvas::OnMouseMoved) EVT_LEFT_DOWN(UITestCanvas::OnMouseDown) EVT_LEFT_UP(UITestCanvas::OnMouseReleased) EVT_LEAVE_WINDOW(UITestCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(UITestCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() UITestCanvas::UITestCanvas(wxWindow *parent, std::vector dispAttrs) : InteractiveCanvas(parent, dispAttrs) { glContext = new UITestContext(this, &wxGetApp().GetContext(this)); } UITestCanvas::~UITestCanvas() { } void UITestCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); glContext->DrawBegin(); glContext->Draw(); glContext->DrawEnd(); SwapBuffers(); } void UITestCanvas::OnIdle(wxIdleEvent& /* event */) { Refresh(false); } void UITestCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); } void UITestCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); } void UITestCanvas::OnMouseWheelMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseWheelMoved(event); } void UITestCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); } void UITestCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); } void UITestCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); } CubicSDR-0.2.3/src/ui/UITestCanvas.h000066400000000000000000000015421322677621400170140ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include #include #include "InteractiveCanvas.h" #include "UITestContext.h" #include "MouseTracker.h" #include "Timer.h" class UITestCanvas: public InteractiveCanvas { public: UITestCanvas(wxWindow *parent, std::vector dispAttrs); ~UITestCanvas(); private: void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); void OnMouseMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); UITestContext *glContext; wxDECLARE_EVENT_TABLE(); }; CubicSDR-0.2.3/src/ui/UITestContext.cpp000066400000000000000000000047521322677621400175660ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "UITestContext.h" #include "UITestCanvas.h" #include "ColorTheme.h" UITestContext::UITestContext(UITestCanvas *canvas, wxGLContext *sharedContext) : PrimaryGLContext(canvas, sharedContext), testMeter("TEST",0,100,50) { testPanel.setPosition(0.0, 0.0); testPanel.setSize(1.0, 1.0); testPanel.setMarginPx(10); testPanel.setFill(GLPanel::GLPANEL_FILL_SOLID); testPanel.setFillColor(RGBA4f(0.0,0.0,1.0)); testChildPanel.setPosition(0.0, 0.0); testChildPanel.setMarginPx(5); testChildPanel.setSize(1.0f, 0.33f); testChildPanel.setCoordinateSystem(GLPanel::GLPANEL_Y_DOWN_ZERO_ONE); testChildPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); testChildPanel.setFillColor(RGBA4f(0.0,0.0,1.0), RGBA4f(0.0,1.0,0.0)); testChildPanel.setBorderPx(1); testChildPanel2.setPosition(0.0f, -0.66f); testChildPanel2.setSize(1.0f, 0.33f); testChildPanel2.setMarginPx(5); testChildPanel2.setFill(GLPanel::GLPANEL_FILL_GRAD_X); testChildPanel2.setFillColor(RGBA4f(0.0,0.0,1.0), RGBA4f(0.0,1.0,0.0)); testChildPanel2.setBorderColor(RGBA4f(1.0,0.0,0.0)); testChildPanel2.setBorderPx(1); testChildPanel3.setPosition(0.0f, 0.66f); testChildPanel3.setSize(1.0f, 0.33f); testChildPanel3.setMarginPx(5); testChildPanel3.setFill(GLPanel::GLPANEL_FILL_GRAD_X); testChildPanel3.setFillColor(RGBA4f(0.0,0.0,1.0), RGBA4f(0.0,1.0,0.0)); testChildPanel3.setBorderColor(RGBA4f(1.0,0.0,0.0)); testChildPanel3.setBorderPx(1); testText1.setText("Testing 123.."); testText1.setFill(GLPanel::GLPANEL_FILL_NONE); testChildPanel2.addChild(&testText1); // testPanel.addChild(&testChildPanel); // testPanel.addChild(&testChildPanel2); // testPanel.addChild(&testChildPanel3); testMeter.setSize(0.1f,0.9f); testPanel.addChild(&testMeter); } void UITestContext::DrawBegin() { glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glClearColor(ThemeMgr::mgr.currentTheme->generalBackground.r, ThemeMgr::mgr.currentTheme->generalBackground.g, ThemeMgr::mgr.currentTheme->generalBackground.b, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_TEXTURE_2D); } void UITestContext::Draw() { testPanel.calcTransform(CubicVR::mat4::identity()); testPanel.draw(); } void UITestContext::DrawEnd() { // glFlush(); // CheckGLError(); } CubicSDR-0.2.3/src/ui/UITestContext.h000066400000000000000000000010531322677621400172220ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "PrimaryGLContext.h" #include "GLPanel.h" #include "MeterPanel.h" class UITestCanvas; class UITestContext: public PrimaryGLContext { public: UITestContext(UITestCanvas *canvas, wxGLContext *sharedContext); void DrawBegin(); void Draw(); void DrawEnd(); private: GLPanel testPanel; GLTestPanel testChildPanel; GLPanel testChildPanel2; GLPanel testChildPanel3; GLTextPanel testText1; MeterPanel testMeter; }; CubicSDR-0.2.3/src/util/000077500000000000000000000000001322677621400146705ustar00rootroot00000000000000CubicSDR-0.2.3/src/util/DataTree.cpp000077500000000000000000001651101322677621400170740ustar00rootroot00000000000000/* * DataElement/DataNode/DataTree -- structured serialization/unserialization system * designed for the CoolMule project :) * Copyright (C) 2003 by Charles J. Cliffe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "DataTree.h" #include #include #include #include #include #include /* DataElement class */ using namespace std; #define STRINGIFY(A) #A DataElement::DataElement() : data_type(DATA_NULL), data_size(0), unit_size(0), data_val(NULL) { } DataElement::DataElement(DataElement &cloneFrom) : data_type(cloneFrom.getDataType()), unit_size(cloneFrom.getUnitSize()) { data_val = NULL; data_init(cloneFrom.getDataSize()); if (data_size) { memcpy(data_val, cloneFrom.getDataPointer(), data_size); } } DataElement::~DataElement() { if (data_val) { delete[] data_val; data_val = NULL; } } void DataElement::data_init(size_t data_size_in) { if (data_val) { delete[] data_val; data_val = NULL; } data_size = data_size_in; if (data_size) { data_val = new char[data_size]; //memset to zero std::fill_n(data_val, data_size, 0); } } char * DataElement::getDataPointer() { return data_val; } int DataElement::getDataType() { return data_type; } size_t DataElement::getDataSize() { return data_size; } unsigned int DataElement::getUnitSize() { return unit_size; } #define DataElementSetNumericDef(enumtype, datatype) void DataElement::set(const datatype& val_in) { \ data_type = enumtype; \ unit_size = sizeof(datatype); \ data_init(unit_size); \ memcpy(data_val, &val_in, data_size); \ } DataElementSetNumericDef(DATA_CHAR, char) DataElementSetNumericDef(DATA_UCHAR, unsigned char) DataElementSetNumericDef(DATA_INT, int) DataElementSetNumericDef(DATA_UINT, unsigned int) DataElementSetNumericDef(DATA_LONG, long) DataElementSetNumericDef(DATA_ULONG, unsigned long) DataElementSetNumericDef(DATA_LONGLONG, long long) DataElementSetNumericDef(DATA_FLOAT, float) DataElementSetNumericDef(DATA_DOUBLE, double) DataElementSetNumericDef(DATA_LONGDOUBLE, long double) void DataElement::set(const char *data_in, long size_in) { data_type = DATA_VOID; if (!data_size) { return; } data_init(size_in); memcpy(data_val, data_in, data_size); } void DataElement::set(const char *data_in) { data_type = DATA_STRING; data_init(strlen(data_in) + 1); memcpy(data_val, data_in, data_size); } void DataElement::set(const string &str_in) { data_type = DATA_STRING; data_init(str_in.length() + 1); memcpy(data_val, str_in.c_str(), data_size); } void DataElement::set(const wstring &wstr_in) { data_type = DATA_WSTRING; //wchar_t is tricky, the terminating zero is actually a (wchar_t)0 ! //wchar_t is typically 16 bits on windows, and 32 bits on Unix, so use sizeof(wchar_t) everywhere. size_t maxLenBytes = (wstr_in.length()+1) * sizeof(wchar_t); //be paranoid, zero the buffer char *tmp_str = (char *)calloc(maxLenBytes, sizeof(char)); //if something awful happens, the last sizeof(wchar_t) is at least zero... wcstombs(tmp_str, wstr_in.c_str(), maxLenBytes - sizeof(wchar_t)); data_init(maxLenBytes); memcpy(data_val, tmp_str, data_size); free(tmp_str); } void DataElement::set(vector &strvect_in) { vector::iterator i; long vectsize; long ptr; data_type = DATA_STR_VECTOR; vectsize = 0; for (i = strvect_in.begin(); i != strvect_in.end(); i++) { vectsize += (*i).length() + 1; } data_init(vectsize); ptr = 0; for (i = strvect_in.begin(); i != strvect_in.end(); i++) { int str_length; str_length = (*i).length() + 1; memcpy(data_val + ptr, (*i).c_str(), str_length); ptr += str_length; } } void DataElement::set(std::set &strset_in) { std::set::iterator i; vector tmp_vect; for (i = strset_in.begin(); i != strset_in.end(); i++) { tmp_vect.push_back(*i); } set(tmp_vect); } #define DataElementSetNumericVectorDef(enumtype, datatype) void DataElement::set(vector& val_in) { \ data_type = enumtype; \ unit_size = sizeof(datatype); \ data_init(unit_size * val_in.size()); \ memcpy(data_val, &val_in[0], data_size); \ } DataElementSetNumericVectorDef(DATA_CHAR_VECTOR, char) DataElementSetNumericVectorDef(DATA_UCHAR_VECTOR, unsigned char) DataElementSetNumericVectorDef(DATA_INT_VECTOR, int) DataElementSetNumericVectorDef(DATA_UINT_VECTOR, unsigned int) DataElementSetNumericVectorDef(DATA_LONG_VECTOR, long) DataElementSetNumericVectorDef(DATA_ULONG_VECTOR, unsigned long) DataElementSetNumericVectorDef(DATA_LONGLONG_VECTOR, long long) DataElementSetNumericVectorDef(DATA_FLOAT_VECTOR, float) DataElementSetNumericVectorDef(DATA_DOUBLE_VECTOR, double) DataElementSetNumericVectorDef(DATA_LONGDOUBLE_VECTOR, long double) #define DataElementGetNumericDef(enumtype, datatype, ...) void DataElement::get(datatype& val_out) { \ if (!data_type) \ return; \ int _compat[] = {__VA_ARGS__}; \ if (data_type != enumtype) { \ bool compat = false; \ for (size_t i = 0; i < sizeof(_compat)/sizeof(int); i++) { \ if (_compat[i] == data_type) { \ compat = true; \ break; \ } \ } \ if (!compat) { \ throw(new DataTypeMismatchException("Type mismatch, element type " #enumtype " is not compatible with a " #datatype)); \ } \ if (sizeof(datatype) < data_size) { \ std::cout << "Warning, data type mismatch requested size for '" << #datatype << "(" << sizeof(datatype) << ")' < data size '" << data_size << "'; possible loss of data."; \ } \ memset(&val_out, 0, sizeof(datatype)); \ if (sizeof(datatype) > 4 && data_size <= 4) { \ int v = 0; memcpy(&v,data_val,data_size); \ val_out = (datatype)v; \ return; \ } else { \ memcpy(&val_out, data_val, (sizeof(datatype) < data_size) ? sizeof(datatype) : data_size); \ } \ return; \ } \ memcpy(&val_out, data_val, data_size); \ } DataElementGetNumericDef(DATA_CHAR, char, DATA_UCHAR, DATA_UINT, DATA_ULONG, DATA_LONGLONG, DATA_LONGDOUBLE, DATA_INT, DATA_LONG) DataElementGetNumericDef(DATA_UCHAR, unsigned char, DATA_CHAR, DATA_UINT, DATA_ULONG, DATA_LONGLONG, DATA_LONGDOUBLE, DATA_INT, DATA_LONG) DataElementGetNumericDef(DATA_UINT, unsigned int, DATA_CHAR, DATA_UCHAR, DATA_ULONG, DATA_LONGLONG, DATA_LONGDOUBLE, DATA_INT, DATA_LONG) DataElementGetNumericDef(DATA_ULONG, unsigned long, DATA_CHAR, DATA_UCHAR, DATA_UINT, DATA_LONGLONG, DATA_LONGDOUBLE, DATA_INT, DATA_LONG) DataElementGetNumericDef(DATA_LONGLONG, long long, DATA_CHAR, DATA_UCHAR, DATA_UINT, DATA_ULONG, DATA_LONGDOUBLE, DATA_INT, DATA_LONG) DataElementGetNumericDef(DATA_LONGDOUBLE, long double, DATA_CHAR, DATA_UCHAR, DATA_UINT, DATA_ULONG, DATA_LONGLONG, DATA_INT, DATA_LONG) DataElementGetNumericDef(DATA_INT, int, DATA_CHAR, DATA_UCHAR, DATA_UINT, DATA_ULONG, DATA_LONGLONG, DATA_LONGDOUBLE, DATA_LONG) DataElementGetNumericDef(DATA_LONG, long, DATA_CHAR, DATA_UCHAR, DATA_UINT, DATA_ULONG, DATA_LONGLONG, DATA_LONGDOUBLE, DATA_INT) #define DataElementGetFloatingPointDef(enumtype, datatype, ...) void DataElement::get(datatype& val_out) { \ if (!data_type) \ return; \ int _compat[] = {__VA_ARGS__}; \ if (data_type != enumtype) { \ bool compat = false; \ for (size_t i = 0; i < sizeof(_compat)/sizeof(int); i++) { \ if (_compat[i] == data_type) { \ compat = true; \ break; \ } \ } \ if (!compat) { \ throw(new DataTypeMismatchException("Type mismatch, element type " #enumtype " is not compatible with a " #datatype)); \ } \ if (data_type == DATA_FLOAT || data_type == DATA_DOUBLE) { \ if (sizeof(datatype) < data_size) { \ std::cout << "Warning, data type mismatch requested size for '" << #datatype << "(" << sizeof(datatype) << ")' < data size '" << data_size << "'; possible loss of data."; \ } \ memset(&val_out, 0, sizeof(datatype)); \ if (sizeof(datatype) > 4 && data_size <= 4) { \ int v = 0; memcpy(&v,data_val,data_size); \ val_out = (datatype)v; \ return; \ } else { \ memcpy(&val_out, data_val, (sizeof(datatype) < data_size) ? sizeof(datatype) : data_size); \ } \ } else { \ long long tmp_int; \ get(tmp_int); \ datatype tmp_float = (float)tmp_int; \ memcpy(&val_out, &tmp_float, sizeof(datatype)); \ } \ return; \ } \ memcpy(&val_out, data_val, data_size); \ } DataElementGetFloatingPointDef(DATA_FLOAT, float, DATA_DOUBLE, DATA_CHAR, DATA_UCHAR, DATA_UINT, DATA_ULONG, DATA_LONGLONG, DATA_LONGDOUBLE, DATA_INT, DATA_LONG) DataElementGetFloatingPointDef(DATA_DOUBLE, double, DATA_FLOAT, DATA_CHAR, DATA_UCHAR, DATA_UINT, DATA_ULONG, DATA_LONGLONG, DATA_LONGDOUBLE, DATA_INT, DATA_LONG) void DataElement::get(char **data_in) { if (data_type != DATA_VOID) throw(new DataTypeMismatchException("Type mismatch, not a CHAR*")); *data_in = new char[data_size]; memcpy(*data_in, data_val, data_size); } void DataElement::get(string &str_in) { if (!data_type) return; if (data_type != DATA_STRING) throw(new DataTypeMismatchException("Type mismatch, not a STRING")); if (!str_in.empty()) // flush the string { str_in.erase(str_in.begin(), str_in.end()); } if (data_val) { str_in.append(data_val); } } void DataElement::get(wstring &wstr_in) { if (!data_type) return; if (data_type != DATA_WSTRING) throw(new DataTypeMismatchException("Type mismatch, not a WSTRING")); // flush the string wstr_in.clear(); if (data_val) { //data_val is an array of bytes holding wchar_t characters, plus a terminating (wchar_t)0 //wchar_t is typically 16 bits on windows, and 32 bits on Unix, so use sizeof(wchar_t) everywhere. int maxNbWchars = (data_size - sizeof(wchar_t)) / sizeof(wchar_t); //be paranoid, zero the buffer wchar_t *tmp_wstr = (wchar_t *)calloc(maxNbWchars + 1, sizeof(wchar_t)); //the last wchar_t is actually zero if anything goes wrong... mbstowcs(tmp_wstr, data_val, maxNbWchars); wstr_in.assign(tmp_wstr); free(tmp_wstr); } } void DataElement::get(vector &strvect_in) { size_t ptr; if (!data_type) return; if (data_type != DATA_STR_VECTOR) throw(new DataTypeMismatchException("Type mismatch, not a STRING VECTOR")); ptr = 0; while (ptr != data_size) { strvect_in.push_back(string(data_val + ptr)); ptr += strlen(data_val + ptr) + 1; } } void DataElement::get(std::set &strset_in) { if (!data_type) return; if (data_type != DATA_STR_VECTOR) throw(new DataTypeMismatchException("Type mismatch, not a STRING VECTOR/SET")); std::vector tmp_vect; std::vector::iterator i; get(tmp_vect); for (i = tmp_vect.begin(); i != tmp_vect.end(); i++) { strset_in.insert(*i); } } #define DataElementGetNumericVectorDef(enumtype, datatype, ...) void DataElement::get(vector& val_out) { \ if (!data_type || !unit_size) return; \ if (data_type != enumtype) { \ int _compat[] = {__VA_ARGS__}; \ bool compat = false; \ for (size_t i = 0; i < sizeof(_compat)/sizeof(int); i++) { \ if (_compat[i] == data_type) { \ compat = true; \ break; \ } \ } \ if (!compat) { \ throw(new DataTypeMismatchException("Type mismatch, element type is not compatible with a " #datatype)); \ } \ if (sizeof(datatype) < unit_size) { \ std::cout << "Warning, data type mismatch for vector<" #datatype ">; " #datatype " size " << sizeof(datatype) << " is less than unit size " << unit_size << "; possible loss of data."; \ } \ datatype temp_val; \ size_t ptr = 0; \ while (ptr < data_size) { \ temp_val = 0; \ memcpy(&temp_val, data_val + ptr, (unit_size > sizeof(datatype))?sizeof(datatype):unit_size); \ val_out.push_back(temp_val); \ ptr += unit_size; \ } \ return; \ } \ val_out.assign(data_val, data_val + (data_size / sizeof(datatype))); \ } DataElementGetNumericVectorDef(DATA_CHAR_VECTOR, char, DATA_UCHAR_VECTOR, DATA_INT_VECTOR, DATA_UINT_VECTOR, DATA_LONG_VECTOR, DATA_ULONG_VECTOR, DATA_LONGLONG_VECTOR); DataElementGetNumericVectorDef(DATA_UCHAR_VECTOR, unsigned char, DATA_CHAR_VECTOR, DATA_INT_VECTOR, DATA_UINT_VECTOR, DATA_LONG_VECTOR, DATA_ULONG_VECTOR, DATA_LONGLONG_VECTOR); DataElementGetNumericVectorDef(DATA_INT_VECTOR, int, DATA_CHAR_VECTOR, DATA_UCHAR_VECTOR, DATA_UINT_VECTOR, DATA_LONG_VECTOR, DATA_ULONG_VECTOR, DATA_LONGLONG_VECTOR); DataElementGetNumericVectorDef(DATA_UINT_VECTOR, unsigned int, DATA_CHAR_VECTOR, DATA_UCHAR_VECTOR, DATA_INT_VECTOR, DATA_LONG_VECTOR, DATA_ULONG_VECTOR, DATA_LONGLONG_VECTOR); DataElementGetNumericVectorDef(DATA_LONG_VECTOR, long, DATA_CHAR_VECTOR, DATA_UCHAR_VECTOR, DATA_INT_VECTOR, DATA_UINT_VECTOR, DATA_ULONG_VECTOR, DATA_LONGLONG_VECTOR); DataElementGetNumericVectorDef(DATA_ULONG_VECTOR, unsigned long, DATA_CHAR_VECTOR, DATA_UCHAR_VECTOR, DATA_INT_VECTOR, DATA_UINT_VECTOR, DATA_LONG_VECTOR, DATA_LONGLONG_VECTOR); DataElementGetNumericVectorDef(DATA_LONGLONG_VECTOR, long long, DATA_CHAR_VECTOR, DATA_UCHAR_VECTOR, DATA_INT_VECTOR, DATA_UINT_VECTOR, DATA_LONG_VECTOR, DATA_ULONG_VECTOR); DataElementGetNumericVectorDef(DATA_FLOAT_VECTOR, float, DATA_DOUBLE_VECTOR, DATA_LONGDOUBLE_VECTOR); DataElementGetNumericVectorDef(DATA_DOUBLE_VECTOR, double, DATA_FLOAT_VECTOR, DATA_LONGDOUBLE_VECTOR); DataElementGetNumericVectorDef(DATA_LONGDOUBLE_VECTOR, long double, DATA_DOUBLE_VECTOR, DATA_FLOAT_VECTOR); std::string DataElement::toString() { int dataType = getDataType(); std::string strValue = ""; try { if (dataType == DATA_STRING) { get(strValue); } else if (dataType == DATA_INT || dataType == DATA_LONG || dataType == DATA_LONGLONG) { long long intSettingValue; get(intSettingValue); strValue = std::to_string(intSettingValue); } else if (dataType == DATA_FLOAT || dataType == DATA_DOUBLE) { double floatSettingValue; get(floatSettingValue); strValue = std::to_string(floatSettingValue); } else if (dataType == DATA_NULL) { strValue = ""; } else if (dataType == DATA_WSTRING) { std::wstring wstr; get(wstr); strValue = *wstr.c_str(); } else { std::cout << "Unhandled DataElement toString for type: " << dataType << std::endl; } } catch (DataTypeMismatchException e) { std::cout << "toString() DataTypeMismatch: " << dataType << std::endl; } return strValue; } long DataElement::getSerializedSize() { return sizeof(int) + sizeof(long) + data_size; } long DataElement::getSerialized(char **ser_str) { long ser_size = getSerializedSize(); *ser_str = new char[ser_size]; char *ser_pointer; ser_pointer = *ser_str; memcpy(ser_pointer, &data_type, sizeof(int)); ser_pointer += sizeof(int); memcpy(ser_pointer, &data_size, sizeof(long)); ser_pointer += sizeof(long); memcpy(ser_pointer, data_val, data_size); return ser_size; } void DataElement::setSerialized(char *ser_str) { char *ser_pointer = ser_str; memcpy(&data_type, ser_pointer, sizeof(unsigned char)); ser_pointer += sizeof(unsigned char); memcpy(&data_size, ser_pointer, sizeof(unsigned int)); ser_pointer += sizeof(unsigned int); data_init(data_size); memcpy(data_val, ser_pointer, data_size); } /* DataNode class */ DataNode::DataNode(): parentNode(NULL), ptr(0) { data_elem = new DataElement(); } DataNode::DataNode(const char *name_in): parentNode(NULL), ptr(0) { node_name = name_in; data_elem = new DataElement(); } DataNode::DataNode(const char *name_in, DataNode &cloneFrom): parentNode(NULL), ptr(0) { node_name = name_in; data_elem = new DataElement(*cloneFrom.element()); // TODO: stack recursion optimization while (cloneFrom.hasAnother()) { DataNode *cNode = cloneFrom.getNext(); newChildCloneFrom(cNode->getName().c_str(), cNode); } } DataNode::DataNode(const char *name_in, DataElement &cloneFrom): parentNode(NULL), ptr(0) { node_name = name_in; data_elem = new DataElement(cloneFrom); } DataNode::~DataNode() { while (children.size()) { DataNode *del = children.back(); children.pop_back(); delete del; } if (data_elem) { delete data_elem; } } void DataNode::setName(const char *name_in) { node_name = name_in; } DataElement *DataNode::element() { return data_elem; } DataNode *DataNode::newChild(const char *name_in) { children.push_back(new DataNode(name_in)); childmap[name_in].push_back(children.back()); children.back()->setParentNode(*this); return children.back(); } DataNode *DataNode::newChild(const char *name_in, DataNode *otherNode) { children.push_back(otherNode); childmap[name_in].push_back(children.back()); children.back()->setParentNode(*this); return children.back(); } DataNode *DataNode::newChildCloneFrom(const char *name_in, DataNode *cloneFrom) { DataNode *cloneNode = new DataNode(name_in, *cloneFrom->element()); children.push_back(cloneNode); childmap[name_in].push_back(children.back()); children.back()->setParentNode(*this); // TODO: stack recursion optimization while (cloneFrom->hasAnother()) { DataNode *cNode = cloneFrom->getNext(); cloneNode->newChildCloneFrom(cNode->getName().c_str(), cNode); } cloneFrom->rewind(); return children.back(); } DataNode *DataNode::child(const char *name_in, int index) { DataNode *child_ret; child_ret = childmap[name_in][index]; if (!child_ret) { stringstream error_str; error_str << "no child '" << index << "' in DataNode '" << node_name << "'"; throw(DataInvalidChildException(error_str.str().c_str())); } return child_ret; } DataNode *DataNode::child(int index) { DataNode *child_ret; child_ret = children[index]; if (!child_ret) { stringstream error_str; error_str << "no child '" << index << "' in DataNode '" << node_name << "'"; throw(DataInvalidChildException(error_str.str().c_str())); } return child_ret; } int DataNode::numChildren() { return children.size(); } int DataNode::numChildren(const char *name_in) { return childmap[name_in].size(); } bool DataNode::hasAnother() { return children.size() != ptr; } bool DataNode::hasAnother(const char *name_in) { return childmap[name_in].size() != childmap_ptr[name_in]; } DataNode *DataNode::getNext() { return child(ptr++); } DataNode *DataNode::getNext(const char *name_in) { return child(name_in, childmap_ptr[name_in]++); } void DataNode::rewind() { ptr = 0; childmap_ptr.erase(childmap_ptr.begin(),childmap_ptr.end()); } void DataNode::rewind(const char *name_in) { childmap_ptr[name_in] = 0; } /* DataTree class */ DataTree::DataTree(const char *name_in) { dn_root.setName(name_in); } DataTree::DataTree() { } DataTree::~DataTree() { } ; DataNode *DataTree::rootNode() { return &dn_root; } std::string trim(std::string& s, const std::string& drop = " ") { std::string r = s.erase(s.find_last_not_of(drop) + 1); return r.erase(0, r.find_first_not_of(drop)); } string DataTree::wsEncode(const wstring& wstr) { stringstream encStream; //wchar_t is typically 16 bits on windows, and 32 bits on Unix, so use sizeof(wchar_t) everywhere. int bufSizeBytes = (wstr.length()+1) * sizeof(wchar_t); char *data_str = (char *)calloc(bufSizeBytes, sizeof(char)); wcstombs(data_str, wstr.c_str(), bufSizeBytes - sizeof(wchar_t)); std::string byte_str(data_str); free(data_str); encStream << std::hex; for(auto i = byte_str.begin(); i != byte_str.end(); i++) { encStream << '%' << setfill('0') << (unsigned int)((unsigned char)(*i)); } return encStream.str(); } wstring DataTree::wsDecode(const string& str) { std::stringstream decStream; std::stringstream mbstr; unsigned int x; string decStr = str; std::replace( decStr.begin(), decStr.end(), '%', ' '); decStream << trim(decStr); string sResult; //this actually assume we will get as many char as wchar_t from the decodes string, //who cares ? int maxLen = decStr.length(); //wchar_t is typically 16 bits on windows, and 32 bits on Unix, so use sizeof(wchar_t) everywhere. wchar_t *wc_str = (wchar_t *) calloc(maxLen + 1, sizeof(wchar_t)); while (!decStream.eof()) { decStream >> std::hex >> x; //extract actually 2 hex-chars by 2 hex-chars to form a char value. mbstr << (unsigned char) x; } mbstowcs(wc_str, mbstr.str().c_str(), maxLen); wstring result(wc_str); free(wc_str); return result; } void DataTree::decodeXMLText(DataNode *elem, const char *src_text, DT_FloatingPointPolicy fpp) { int tmp_char; int tmp_int; long tmp_long; long long tmp_llong; double tmp_double; float tmp_float; string tmp_str; string tmp_str2; std::stringstream tmp_stream; std::stringstream tmp_stream2; vector tmp_charvect; vector tmp_intvect; vector tmp_longvect; vector tmp_llongvect; vector::iterator tmp_llongvect_i; vector tmp_doublevect; vector::iterator tmp_doublevect_i; vector tmp_floatvect; bool vChars = false; bool vInts = false; bool vLongs = false; string in_text = src_text; trim(in_text); trim(in_text, "\r\n"); tmp_stream.str(""); tmp_stream2.str(""); if (in_text.find_first_not_of("0123456789-") == string::npos) { tmp_stream << in_text; tmp_stream >> tmp_llong; tmp_int = (int)tmp_llong; tmp_long = (long)tmp_llong; if (tmp_int == tmp_llong) { elem->element()->set(tmp_int); } else if (tmp_long == tmp_llong) { elem->element()->set(tmp_long); } else { elem->element()->set(tmp_llong); } } else if (in_text.find_first_not_of("0123456789.e+-") == string::npos) { tmp_stream << in_text; if (fpp == USE_FLOAT) { tmp_stream >> tmp_float; elem->element()->set((float) tmp_float); } else { tmp_stream >> tmp_double; elem->element()->set((double) tmp_double); } } else if (in_text.find_first_not_of("0123456789- ") == string::npos) { tmp_stream << in_text; vChars = true; vInts = true; vLongs = true; while (!tmp_stream.eof()) { tmp_stream >> tmp_llong; tmp_char = tmp_llong; tmp_int = tmp_llong; tmp_long = tmp_llong; if (tmp_char != tmp_llong) { vChars = false; } if (tmp_int != tmp_llong) { vInts = false; } if (tmp_long != tmp_llong) { vLongs = false; } tmp_llongvect.push_back((long) tmp_long); } if (vChars) { for (tmp_llongvect_i = tmp_llongvect.begin(); tmp_llongvect_i != tmp_llongvect.end(); tmp_llongvect_i++) { tmp_charvect.push_back(*tmp_llongvect_i); } tmp_llongvect.clear(); elem->element()->set(tmp_charvect); tmp_charvect.clear(); } else if (vInts) { for (tmp_llongvect_i = tmp_llongvect.begin(); tmp_llongvect_i != tmp_llongvect.end(); tmp_llongvect_i++) { tmp_intvect.push_back(*tmp_llongvect_i); } tmp_llongvect.clear(); elem->element()->set(tmp_intvect); tmp_intvect.clear(); } else if (vLongs) { for (tmp_llongvect_i = tmp_llongvect.begin(); tmp_llongvect_i != tmp_llongvect.end(); tmp_llongvect_i++) { tmp_longvect.push_back(*tmp_llongvect_i); } tmp_llongvect.clear(); elem->element()->set(tmp_longvect); tmp_longvect.clear(); } else { elem->element()->set(tmp_llongvect); } } else if (in_text.find_first_not_of("0123456789.e-+ ") == string::npos) { tmp_stream << in_text; if (fpp == USE_FLOAT) { tmp_floatvect.clear(); } else { tmp_doublevect.clear(); } while (!tmp_stream.eof()) { if (fpp == USE_FLOAT) { tmp_stream >> tmp_float; tmp_floatvect.push_back(tmp_float); } else { tmp_stream >> tmp_double; tmp_doublevect.push_back(tmp_double); } } if (fpp == USE_FLOAT) { elem->element()->set(tmp_floatvect); } else { elem->element()->set(tmp_doublevect); } } else if (in_text.find_first_not_of("0123456789abcdef%") == string::npos) { elem->element()->set(wsDecode(src_text)); } else { elem->element()->set(src_text); // printf( "Unhandled DataTree XML Field: [%s]", tmp_str.c_str() ); } } void DataTree::setFromXML(DataNode *elem, TiXmlNode *elxml, bool root_node, DT_FloatingPointPolicy fpp) { TiXmlText *pText; int t = elxml->Type(); string tmp_str; switch (t) { case TiXmlNode::TINYXML_DOCUMENT: // printf( "Document" ); break; case TiXmlNode::TINYXML_ELEMENT: if (!root_node) elem = elem->newChild(elxml->Value()); const TiXmlAttribute *attribs; attribs = elxml->ToElement()->FirstAttribute(); while (attribs) { // following badgerfish xml->json and xml->ruby convention for attributes.. string attrName("@"); attrName.append(attribs->Name()); decodeXMLText(elem->newChild(attrName.c_str()), attribs->Value(), fpp); attribs = attribs->Next(); } // printf( "Element \"%s\"", elxml->Value()); break; case TiXmlNode::TINYXML_COMMENT: // printf( "Comment: \"%s\"", elxml->Value()); break; case TiXmlNode::TINYXML_UNKNOWN: // printf( "Unknown" ); break; case TiXmlNode::TINYXML_TEXT: pText = elxml->ToText(); decodeXMLText(elem, pText->Value(), fpp); // pText = elxml->ToText(); // printf( "Text: [%s]", pText->Value() ); break; case TiXmlNode::TINYXML_DECLARATION: // printf( "Declaration" ); break; default: break; } // printf( "\n" ); TiXmlNode * pChild; if (!elxml->NoChildren()) { if (elxml->FirstChild()->Type() == TiXmlNode::TINYXML_ELEMENT) { if (elxml->FirstChild()->Value() == TIXML_STRING("str")) { std::vector tmp_strvect; for (pChild = elxml->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { if (pChild->Value() == TIXML_STRING("str")) { if (!pChild->FirstChild()) { tmp_strvect.push_back(""); continue; } pText = pChild->FirstChild()->ToText(); if (pText) { tmp_str = pText->Value(); tmp_strvect.push_back(tmp_str); } } } elem->element()->set(tmp_strvect); return; } } } for (pChild = elxml->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { setFromXML(elem, pChild, false, fpp); } } void DataTree::nodeToXML(DataNode *elem, TiXmlElement *elxml) { DataNode *child; elem->rewind(); while (elem->hasAnother()) { child = elem->getNext(); std::string nodeName = child->getName(); TiXmlElement *element; element = new TiXmlElement(nodeName.length() ? nodeName.c_str() : "node"); std::string tmp; std::wstring wtmp; std::stringstream tmp_stream; TiXmlText *text; std::vector tmp_floatvect; std::vector::iterator tmp_floatvect_i; std::vector tmp_doublevect; std::vector::iterator tmp_doublevect_i; std::vector tmp_intvect; std::vector::iterator tmp_intvect_i; std::vector tmp_charvect; std::vector::iterator tmp_charvect_i; std::vector tmp_ucharvect; std::vector::iterator tmp_ucharvect_i; std::vector tmp_uintvect; std::vector::iterator tmp_uintvect_i; std::vector tmp_longvect; std::vector::iterator tmp_longvect_i; std::vector tmp_ulongvect; std::vector::iterator tmp_ulongvect_i; std::vector tmp_llongvect; std::vector::iterator tmp_llongvect_i; std::vector tmp_ullongvect; std::vector::iterator tmp_ullongvect_i; std::vector tmp_stringvect; std::vector::iterator tmp_stringvect_i; TiXmlElement *tmp_node; char *tmp_pstr; double tmp_double; long double tmp_ldouble; float tmp_float; char tmp_char; unsigned char tmp_uchar; int tmp_int; unsigned int tmp_uint; long tmp_long; unsigned long tmp_ulong; long long tmp_llong; switch (child->element()->getDataType()) { case DATA_NULL: break; case DATA_VOID: child->element()->get(&tmp_pstr); // following badgerfish xml->json and xml->ruby convention for attributes.. if (nodeName.substr(0, 1) == string("@")) { elxml->SetAttribute(nodeName.substr(1).c_str(), tmp_pstr); delete element; element = NULL; } else { text = new TiXmlText(tmp_pstr); element->LinkEndChild(text); } delete[] tmp_pstr; break; case DATA_CHAR: child->element()->get(tmp_char); tmp_stream.str(""); tmp_stream << tmp_char; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_UCHAR: child->element()->get(tmp_uchar); tmp_stream.str(""); tmp_stream << tmp_uchar; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_INT: child->element()->get(tmp_int); tmp_stream.str(""); tmp_stream << tmp_int; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_UINT: child->element()->get(tmp_uint); tmp_stream.str(""); tmp_stream << tmp_uint; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_LONG: child->element()->get(tmp_long); tmp_stream.str(""); tmp_stream << tmp_long; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_ULONG: child->element()->get(tmp_ulong); tmp_stream.str(""); tmp_stream << tmp_ulong; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_LONGLONG: child->element()->get(tmp_llong); tmp_stream.str(""); tmp_stream << tmp_llong; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_FLOAT: child->element()->get(tmp_float); tmp_stream.str(""); tmp_stream << tmp_float; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_DOUBLE: child->element()->get(tmp_double); tmp_stream.str(""); tmp_stream << tmp_double; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_LONGDOUBLE: child->element()->get(tmp_ldouble); tmp_stream.str(""); tmp_stream << tmp_ldouble; text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); break; case DATA_STRING: child->element()->get(tmp); if (nodeName.substr(0, 1) == string("@")) { elxml->SetAttribute(nodeName.substr(1).c_str(), tmp.c_str()); delete element; element = NULL; } else { text = new TiXmlText(tmp.c_str()); element->LinkEndChild(text); } break; case DATA_WSTRING: child->element()->get(wtmp); tmp = wsEncode(wtmp); if (nodeName.substr(0, 1) == string("@")) { elxml->SetAttribute(nodeName.substr(1).c_str(), tmp.c_str()); delete element; element = NULL; } else { text = new TiXmlText(tmp.c_str()); element->LinkEndChild(text); } break; case DATA_STR_VECTOR: child->element()->get(tmp_stringvect); tmp_stream.str(""); for (tmp_stringvect_i = tmp_stringvect.begin(); tmp_stringvect_i != tmp_stringvect.end(); tmp_stringvect_i++) { tmp_node = new TiXmlElement("str"); text = new TiXmlText((*tmp_stringvect_i).c_str()); tmp_node->LinkEndChild(text); element->LinkEndChild(tmp_node); } tmp_stringvect.clear(); break; case DATA_CHAR_VECTOR: child->element()->get(tmp_charvect); tmp_stream.str(""); for (tmp_charvect_i = tmp_charvect.begin(); tmp_charvect_i != tmp_charvect.end(); tmp_charvect_i++) { tmp_stream << (*tmp_charvect_i); if (tmp_charvect_i != tmp_charvect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_charvect.clear(); break; case DATA_UCHAR_VECTOR: child->element()->get(tmp_ucharvect); tmp_stream.str(""); for (tmp_ucharvect_i = tmp_ucharvect.begin(); tmp_ucharvect_i != tmp_ucharvect.end(); tmp_ucharvect_i++) { tmp_stream << (*tmp_ucharvect_i); if (tmp_ucharvect_i != tmp_ucharvect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_ucharvect.clear(); break; case DATA_INT_VECTOR: child->element()->get(tmp_intvect); tmp_stream.str(""); for (tmp_intvect_i = tmp_intvect.begin(); tmp_intvect_i != tmp_intvect.end(); tmp_intvect_i++) { tmp_stream << (*tmp_intvect_i); if (tmp_intvect_i != tmp_intvect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_intvect.clear(); break; case DATA_UINT_VECTOR: child->element()->get(tmp_uintvect); tmp_stream.str(""); for (tmp_uintvect_i = tmp_uintvect.begin(); tmp_uintvect_i != tmp_uintvect.end(); tmp_uintvect_i++) { tmp_stream << (*tmp_intvect_i); if (tmp_uintvect_i != tmp_uintvect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_uintvect.clear(); break; case DATA_LONG_VECTOR: child->element()->get(tmp_longvect); tmp_stream.str(""); for (tmp_longvect_i = tmp_longvect.begin(); tmp_longvect_i != tmp_longvect.end(); tmp_longvect_i++) { tmp_stream << (*tmp_longvect_i); if (tmp_longvect_i != tmp_longvect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_longvect.clear(); break; case DATA_ULONG_VECTOR: child->element()->get(tmp_ulongvect); tmp_stream.str(""); for (tmp_ulongvect_i = tmp_ulongvect.begin(); tmp_ulongvect_i != tmp_ulongvect.end(); tmp_ulongvect_i++) { tmp_stream << (*tmp_ulongvect_i); if (tmp_ulongvect_i != tmp_ulongvect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_ulongvect.clear(); break; case DATA_LONGLONG_VECTOR: child->element()->get(tmp_llongvect); tmp_stream.str(""); for (tmp_llongvect_i = tmp_llongvect.begin(); tmp_llongvect_i != tmp_llongvect.end(); tmp_llongvect_i++) { tmp_stream << (*tmp_llongvect_i); if (tmp_llongvect_i != tmp_llongvect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_llongvect.clear(); break; case DATA_FLOAT_VECTOR: child->element()->get(tmp_floatvect); tmp_stream.str(""); for (tmp_floatvect_i = tmp_floatvect.begin(); tmp_floatvect_i != tmp_floatvect.end(); tmp_floatvect_i++) { tmp_stream << (*tmp_floatvect_i); if (tmp_floatvect_i != tmp_floatvect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_floatvect.clear(); break; case DATA_DOUBLE_VECTOR: child->element()->get(tmp_doublevect); tmp_stream.str(""); for (tmp_doublevect_i = tmp_doublevect.begin(); tmp_doublevect_i != tmp_doublevect.end(); tmp_doublevect_i++) { tmp_stream << (*tmp_doublevect_i); if (tmp_doublevect_i != tmp_doublevect.end() - 1) tmp_stream << " "; } text = new TiXmlText(tmp_stream.str().c_str()); element->LinkEndChild(text); tmp_doublevect.clear(); break; } if (element) { elxml->LinkEndChild(element); if (child->numChildren()) { nodeToXML(child, element); } } } elem->rewind(); } void DataTree::printXML() /* get serialized size + return node names header */ { TiXmlDocument doc; TiXmlDeclaration * decl = new TiXmlDeclaration("1.0", "", ""); doc.LinkEndChild(decl); DataNode *root = rootNode(); string rootName = root->getName(); TiXmlElement *element = new TiXmlElement(rootName.empty() ? "root" : rootName.c_str()); doc.LinkEndChild(element); if (!root->numChildren()) doc.Print(); nodeToXML(root, element); root->rewind(); doc.Print(); } long DataTree::getSerializedSize(DataElement &de_node_names, bool debug) /* get serialized size + return node names header */ { long total_size = 0; stack dn_stack; vector node_names; map node_name_index_map; DataElement de_name_index; // just used for sizing purposes DataElement de_num_children; de_name_index.set((int) 0); de_num_children.set((int) 0); int de_name_index_size = de_name_index.getSerializedSize(); int de_num_children_size = de_num_children.getSerializedSize(); dn_stack.push(&dn_root); while (!dn_stack.empty()) { int name_index; // int num_children; /* build the name list */ if (dn_stack.top()->getName().empty()) { name_index = 0; /* empty string */ } else if (node_name_index_map[dn_stack.top()->getName().c_str()] == 0) { node_names.push_back(string(dn_stack.top()->getName())); name_index = node_names.size(); node_name_index_map[dn_stack.top()->getName().c_str()] = name_index; } else { name_index = node_name_index_map[dn_stack.top()->getName().c_str()]; } /* add on the size of the name index and number of children */ total_size += de_name_index_size; total_size += de_num_children_size; total_size += dn_stack.top()->element()->getSerializedSize(); /* debug output */ if (debug) { for (unsigned int i = 0; i < dn_stack.size() - 1; i++) cout << "--"; cout << (dn_stack.top()->getName().empty() ? "NULL" : dn_stack.top()->getName()) << "(" << dn_stack.top()->element()->getSerializedSize() << ")"; cout << " type: " << dn_stack.top()->element()->getDataType() << endl; //cout << " index: " << name_index << endl; } /* end debug output */ /* if it has children, traverse into them */ if (dn_stack.top()->hasAnother()) { dn_stack.push(dn_stack.top()->getNext()); dn_stack.top()->rewind(); } else { /* no more children, back out until we have children, then add next child to the top */ while (!dn_stack.empty()) { if (!dn_stack.top()->hasAnother()) { dn_stack.top()->rewind(); dn_stack.pop(); } else break; } if (!dn_stack.empty()) { dn_stack.push(dn_stack.top()->getNext()); dn_stack.top()->rewind(); } } } /* set the header for use in serialization */ de_node_names.set(node_names); total_size += de_node_names.getSerializedSize(); return total_size; } void DataNode::rewindAll() { stack dn_stack; /* start at the root */ dn_stack.push(this); while (!dn_stack.empty()) { dn_stack.top()->rewind(); /* if it has children, traverse into them */ if (dn_stack.top()->hasAnother()) { dn_stack.push(dn_stack.top()->getNext()); dn_stack.top()->rewind(); } else { /* no more children, back out until we have children, then add next child to the top */ while (!dn_stack.empty()) { if (!dn_stack.top()->hasAnother()) { dn_stack.top()->rewind(); dn_stack.pop(); } else break; } if (!dn_stack.empty()) { dn_stack.push(dn_stack.top()->getNext()); dn_stack.top()->rewind(); } } } } void DataNode::findAll(const char *name_in, vector &node_list_out) { stack dn_stack; /* start at the root */ dn_stack.push(this); if (string(getName()) == string(name_in)) node_list_out.push_back(this); while (!dn_stack.empty()) { while (dn_stack.top()->hasAnother(name_in)) { node_list_out.push_back(dn_stack.top()->getNext(name_in)); } /* if it has children, traverse into them */ if (dn_stack.top()->hasAnother()) { dn_stack.push(dn_stack.top()->getNext()); dn_stack.top()->rewind(); } else { /* no more children, back out until we have children, then add next child to the top */ while (!dn_stack.empty()) { if (!dn_stack.top()->hasAnother()) { dn_stack.top()->rewind(); dn_stack.pop(); } else break; } if (!dn_stack.empty()) { dn_stack.push(dn_stack.top()->getNext()); dn_stack.top()->rewind(); } } } } long DataTree::getSerialized(char **ser_str, bool debug) { long data_ptr = 0; long data_size = 0; stack dn_stack; vector node_names; map node_name_index_map; /* header of node names, grabbed from getserializedsize to avoid having to memmove() or realloc() */ DataElement de_node_names; data_size = getSerializedSize(de_node_names, debug); *ser_str = (char *) malloc(data_size); char *data_out = *ser_str; /* name list header */ char *de_node_names_serialized; long de_node_names_serialized_size; de_node_names.getSerialized(&de_node_names_serialized); de_node_names_serialized_size = de_node_names.getSerializedSize(); /* copy the header and increase the pointer */ memcpy(data_out, de_node_names_serialized, de_node_names_serialized_size); data_ptr += de_node_names_serialized_size; /* start at the root */ dn_stack.push(&dn_root); while (!dn_stack.empty()) { int name_index; int num_children; DataElement de_name_index; DataElement de_num_children; char *de_name_index_serialized; char *de_num_children_serialized; char *element_serialized; long de_name_index_serialized_size; long de_num_children_serialized_size; long element_serialized_size; /* build the name list */ if (dn_stack.top()->getName().empty()) { name_index = 0; /* empty string */ } else if (node_name_index_map[dn_stack.top()->getName().c_str()] == 0) { node_names.push_back(string(dn_stack.top()->getName())); name_index = node_names.size(); node_name_index_map[dn_stack.top()->getName().c_str()] = name_index; } else { name_index = node_name_index_map[dn_stack.top()->getName().c_str()]; } num_children = dn_stack.top()->numChildren(); de_name_index.set(name_index); de_num_children.set(num_children); de_name_index_serialized_size = de_name_index.getSerializedSize(); de_num_children_serialized_size = de_num_children.getSerializedSize(); element_serialized_size = dn_stack.top()->element()->getSerializedSize(); de_name_index.getSerialized(&de_name_index_serialized); de_num_children.getSerialized(&de_num_children_serialized); dn_stack.top()->element()->getSerialized(&element_serialized); /* add on the name index and number of children */ memcpy(data_out + data_ptr, de_name_index_serialized, de_name_index_serialized_size); data_ptr += de_name_index_serialized_size; memcpy(data_out + data_ptr, de_num_children_serialized, de_num_children_serialized_size); data_ptr += de_num_children_serialized_size; /* add on the data element */ memcpy(data_out + data_ptr, element_serialized, element_serialized_size); data_ptr += element_serialized_size; delete[] de_name_index_serialized; delete[] de_num_children_serialized; delete[] element_serialized; /* if it has children, traverse into them */ if (dn_stack.top()->hasAnother()) { dn_stack.push(dn_stack.top()->getNext()); dn_stack.top()->rewind(); } else { /* no more children, back out until we have children, then add next child to the top */ while (!dn_stack.empty()) { if (!dn_stack.top()->hasAnother()) { dn_stack.top()->rewind(); dn_stack.pop(); } else break; } if (!dn_stack.empty()) { dn_stack.push(dn_stack.top()->getNext()); dn_stack.top()->rewind(); } } } return data_size; } void DataTree::setSerialized(char *ser_str, bool debug) { long data_ptr = 0; // long data_size = 0; stack dn_stack; stack dn_childcount_stack; vector node_names; DataElement de_node_names; de_node_names.setSerialized(ser_str); data_ptr += de_node_names.getSerializedSize(); de_node_names.get(node_names); DataElement de_name_index; DataElement de_num_children; DataElement de_element; dn_stack.push(&dn_root); dn_childcount_stack.push(0); /* root (parent null) has no siblings */ /* unserialization is a little less straightforward since we have to do a countdown of remaining children */ while (!dn_stack.empty()) { int name_index = 0; int num_children = 0; /* pull the index of the name of this node */ de_name_index.setSerialized(ser_str + data_ptr); data_ptr += de_name_index.getSerializedSize(); /* pull the number of children this node has */ de_num_children.setSerialized(ser_str + data_ptr); data_ptr += de_num_children.getSerializedSize(); /* get values from the temp dataelements */ de_name_index.get(name_index); de_num_children.get(num_children); /* pull the node's element */ dn_stack.top()->element()->setSerialized(ser_str + data_ptr); data_ptr += dn_stack.top()->element()->getSerializedSize(); /* debug output */ if (debug) { for (unsigned int i = 0; i < dn_stack.size() - 1; i++) cout << "--"; cout << (name_index ? node_names[name_index - 1] : "NULL") << "(" << dn_stack.top()->element()->getSerializedSize() << ")"; cout << " index: " << name_index << endl; } /* end debug output */ /* name index >= 1 means it has a name */ if (name_index >= 1) { dn_stack.top()->setName(node_names[name_index - 1].c_str()); } else /* name is nil */ { dn_stack.top()->setName(""); } if (num_children) /* Has children, create first child and push it to the top */ { dn_childcount_stack.push(num_children); /* push the child count onto the stack */ de_name_index.setSerialized(ser_str + data_ptr); /* peek at the new child name but don't increment pointer */ de_name_index.get(name_index); /* add this child onto the top of the stack */ dn_stack.push(dn_stack.top()->newChild((name_index ? node_names[name_index - 1] : string("")).c_str())); dn_childcount_stack.top()--; /* decrement to count the new child */ } else /* No children, move on to the next sibling */ { if (dn_childcount_stack.top()) /* any siblings remaining? */ { de_name_index.setSerialized(ser_str + data_ptr); /* peek at the new child name but don't increment pointer */ de_name_index.get(name_index); dn_stack.pop(); dn_stack.push(dn_stack.top()->newChild((name_index ? node_names[name_index - 1] : string("")).c_str())); /* create the next sibling and throw it on the stack */ dn_childcount_stack.top()--; /* decrement to count the new sibling */ } else /* This is the last sibling, move up the stack and find the next */ { while (!dn_stack.empty()) /* move up the stack until we find the next sibling */ { if (dn_childcount_stack.top()) { de_name_index.setSerialized(ser_str + data_ptr); /* peek at the new child name but don't increment pointer */ de_name_index.get(name_index); dn_stack.pop(); dn_stack.push(dn_stack.top()->newChild((name_index ? node_names[name_index - 1] : string("")).c_str())); /* throw it on the stack */ dn_childcount_stack.top()--; /* count it */ break ; } else { dn_childcount_stack.pop(); dn_stack.pop(); /* if no more siblings found the stack will empty naturally */ } } } } } } bool DataTree::LoadFromFileXML(const std::string& filename, DT_FloatingPointPolicy fpp) { TiXmlDocument doc(filename.c_str()); bool loadOkay = doc.LoadFile(); if (!loadOkay) { std::cout << "LoadFromFileXML[error loading]: " << filename << std::endl; return false; } TiXmlNode *xml_root_node = doc.RootElement(); if (!xml_root_node) { std::cout << "LoadFromFileXML[error no root]: " << filename << std::endl; return false; } rootNode()->setName(xml_root_node->ToElement()->Value()); setFromXML(rootNode(), xml_root_node, true, fpp); return true; } bool DataTree::SaveToFileXML(const std::string& filename) { TiXmlDocument doc; TiXmlDeclaration * decl = new TiXmlDeclaration("1.0", "", ""); doc.LinkEndChild(decl); string rootName = rootNode()->getName(); TiXmlElement *element = new TiXmlElement(rootName.empty() ? "root" : rootName.c_str()); doc.LinkEndChild(element); nodeToXML(rootNode(), element); doc.SaveFile(filename.c_str()); return true; } /* bool DataTree::SaveToFile(const std::string& filename) { char *serialized; long dataSize = getSerialized(&serialized); std::ofstream fout(filename.c_str(), ios::binary); fout.write(serialized, dataSize); fout << flush; fout.close(); delete serialized; return true; } bool DataTree::LoadFromFile(const std::string& filename) { char *serialized; long dataSize; ifstream fin(filename.c_str(), ios::binary); fin.seekg (0, ios::end); dataSize = fin.tellg(); fin.seekg (0, ios::beg); serialized = new char[dataSize]; fin.read(serialized,dataSize); fin.close(); setSerialized(serialized); delete serialized; return true; } */ bool DataTree::SaveToFile(const std::string& filename, bool compress, int /* compress_level */) { long dataSize, compressedSize = 0, headerSize; char *serialized = nullptr, *hdr_serialized = nullptr, *compressed = nullptr; DataTree dtHeader; dataSize = getSerialized(&serialized); #if USE_FASTLZ if (compress) { compressed = new char[(int) ceil(dataSize * 1.5)]; compressedSize = fastlz_compress_level(compress_level, serialized, dataSize, compressed); compressed = (char *) realloc(compressed, compressedSize); delete serialized; } #else if (compress) { std::cout << "Can't compress, FASTLZ disabled"; compress = false; } #endif DataNode *header = dtHeader.rootNode(); *header->newChild("version") = 1.0f; *header->newChild("compression") = string(compress ? "FastLZ" : "none"); *header->newChild("uncompressed_size") = dataSize; headerSize = dtHeader.getSerialized(&hdr_serialized); std::ofstream fout(filename.c_str(), ios::binary); fout.write((char *) &headerSize, sizeof(long)); fout.write((char *) &(compress ? compressedSize : dataSize), sizeof(long)); fout.write(hdr_serialized, headerSize); fout.write(compress ? compressed : serialized, compress ? compressedSize : dataSize); fout << flush; fout.close(); free(hdr_serialized); if (!compress) { free(serialized); } else { delete[] compressed; } return true; } bool DataTree::LoadFromFile(const std::string& filename) { #if USE_FASTLZ char *compressed; #endif char *serialized, *hdr_serialized; long dataSize, headerSize, compressedSize; ifstream fin(filename.c_str(), ios::binary); fin.read((char *) &headerSize, sizeof(long)); fin.read((char *) &compressedSize, sizeof(long)); hdr_serialized = new char[headerSize]; fin.read(hdr_serialized, headerSize); DataTree dtHeader; dtHeader.setSerialized(hdr_serialized); DataNode *header = dtHeader.rootNode(); string compressionType(*header->getNext("compression")); dataSize = *header->getNext("uncompressed_size"); #if USE_FASTLZ bool uncompress = false; if (compressionType == "FastLZ") { uncompress = true; } if (uncompress) { compressed = new char[compressedSize]; fin.read(compressed, compressedSize); serialized = new char[dataSize]; fastlz_decompress(compressed, compressedSize, serialized, dataSize); delete[] compressed; } else { serialized = new char[dataSize]; fin.read(serialized, dataSize); } #else if (compressionType == "FastLZ") { std::cout << "DataTree Unable to load FastLZ compressed file -- FastLZ is disabled"; return false; } serialized = new char[dataSize]; fin.read(serialized, dataSize); #endif fin.close(); setSerialized(serialized); delete[] serialized; delete[] hdr_serialized; return true; } CubicSDR-0.2.3/src/util/DataTree.h000077500000000000000000000321271322677621400165420ustar00rootroot00000000000000#pragma once /* * DataElement/DataNode/DataTree -- structured serialization/deserialization system * designed for the CoolMule project :) * Copyright (C) 2003 by Charles J. Cliffe 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. */ #define USE_FASTLZ 0 #include #include #include #include #include #include #include #include "tinyxml.h" #if USE_FASTLZ #include "fastlz.h" #endif using namespace std; /* type defines */ #define DATA_NULL 0 #define DATA_CHAR 1 #define DATA_UCHAR 2 #define DATA_INT 3 #define DATA_UINT 4 #define DATA_LONG 5 #define DATA_ULONG 6 #define DATA_LONGLONG 7 #define DATA_FLOAT 8 #define DATA_DOUBLE 9 #define DATA_LONGDOUBLE 10 #define DATA_STRING 11 #define DATA_STR_VECTOR 12 #define DATA_CHAR_VECTOR 13 #define DATA_UCHAR_VECTOR 14 #define DATA_INT_VECTOR 15 #define DATA_UINT_VECTOR 16 #define DATA_LONG_VECTOR 17 #define DATA_ULONG_VECTOR 18 #define DATA_LONGLONG_VECTOR 19 #define DATA_FLOAT_VECTOR 20 #define DATA_DOUBLE_VECTOR 21 #define DATA_LONGDOUBLE_VECTOR 22 #define DATA_VOID 23 #define DATA_WSTRING 24 /* map comparison function */ struct string_less : public std::binary_function { bool operator()(const std::string& a,const std::string& b) const { return a.compare(b) < 0; } }; /* int comparison function */ struct int_less : public std::binary_function { bool operator()(int a,int b) const { return a < b; } }; /* Data Exceptions */ class DataException { private: string reason; public: DataException(const char *why) : reason(why) {} string what() { return reason; } operator string() { return reason; } }; class DataTypeMismatchException : public DataException { public: DataTypeMismatchException(const char *why) : DataException(why) { } }; class DataInvalidChildException : public DataException { public: DataInvalidChildException(const char *why) : DataException(why) { } }; class DataElement { private: int data_type; size_t data_size; unsigned int unit_size; char *data_val; void data_init(size_t data_size_in); public: DataElement(); DataElement(DataElement &cloneFrom); ~DataElement(); int getDataType(); char *getDataPointer(); size_t getDataSize(); unsigned int getUnitSize(); /* set overloads */ void set(const char &char_in); void set(const unsigned char &uchar_in); void set(const int &int_in); void set(const unsigned int &uint_in); void set(const long &long_in); void set(const unsigned long &ulong_in); void set(const long long &llong_in); void set(const float &float_in); void set(const double &double_in); void set(const long double &ldouble_in); void set(const char *data_in, long size_in); /* voids, file chunks anyone? */ void set(const char *data_in); /* strings, stops at NULL, returns as string */ void set(const string &str_in); void set(const wstring &wstr_in); void set(vector &strvect_in); void set(std::set &strset_in); void set(vector &charvect_in); void set(vector &ucharvect_in); void set(vector &intvect_in); void set(vector &uintvect_in); void set(vector &longvect_in); void set(vector &ulongvect_in); void set(vector &llongvect_in); void set(vector &floatvect_in); void set(vector &doublevect_in); void set(vector &ldoublevect_in); /* get overloads */ void get(char &char_in); void get(unsigned char &uchar_in); void get(int &int_in); void get(unsigned int &uint_in); void get(long &long_in); void get(unsigned long &ulong_in); void get(long long &long_in); void get(float &float_in); void get(double &double_in); void get(long double &ldouble_in); void get(char **data_in); /* getting a void or string */ void get(string &str_in); void get(wstring &wstr_in); void get(std::set &strset_in); void get(vector &strvect_in); void get(vector &charvect_in); void get(vector &ucharvect_in); void get(vector &intvect_in); void get(vector &uintvect_in); void get(vector &longvect_in); void get(vector &ulongvect_in); void get(vector &llongvect_in); void get(vector &floatvect_in); void get(vector &doublevect_in); void get(vector &ldoublevect_in); /* special get functions, saves creating unnecessary vars */ int getChar() { char i_get; get(i_get); return i_get; }; unsigned int getUChar() { unsigned char i_get; get(i_get); return i_get; }; int getInt() { int i_get; get(i_get); return i_get; }; unsigned int getUInt() { unsigned int i_get; get(i_get); return i_get; }; long getLong() { long l_get; get(l_get); return l_get; }; unsigned long getULong() { unsigned long l_get; get(l_get); return l_get; }; long getLongLong() { long long l_get; get(l_get); return l_get; }; float getFloat() { float f_get; get(f_get); return f_get; }; double getDouble() { double d_get; get(d_get); return d_get; }; long double getLongDouble() { long double d_get; get(d_get); return d_get; }; std::string toString(); /* serialize functions */ long getSerializedSize(); long getSerialized(char **ser_str); void setSerialized(char *ser_str); }; class DataNode { private: DataNode *parentNode; vector children; map, string_less> childmap; map childmap_ptr; string node_name; DataElement *data_elem; unsigned int ptr; public: DataNode(); DataNode(const char *name_in); DataNode(const char *name_in, DataElement &cloneFrom); DataNode(const char *name_in, DataNode &cloneFrom); ~DataNode(); void setName(const char *name_in); string &getName() { return node_name; } DataNode *getParentNode() { return parentNode; }; void setParentNode(DataNode &parentNode_in) { parentNode = &parentNode_in; }; int numChildren(); /* Number of children */ int numChildren(const char *name_in); /* Number of children named 'name_in' */ DataElement *element(); /* DataElement at this node */ DataNode *newChild(const char *name_in); DataNode *newChild(const char *name_in, DataNode *otherNode); DataNode *newChildCloneFrom(const char *name_in, DataNode *cloneFrom); DataNode *child(const char *name_in, int index = 0); DataNode *child(int index); bool hasAnother(const char *name_in); /* useful for while() loops in conjunction with getNext() */ bool hasAnother(); DataNode *getNext(const char *name_in); /* get next of specified name */ DataNode *getNext(); /* get next child */ void rewind(const char *name_in); /* rewind specific */ void rewind(); /* rewind generic */ void rewindAll(); void findAll(const char *name_in, vector &node_list_out); // operator string () { string s; element()->get(s); return s; } operator const char * () { if (element()->getDataType() == DATA_STRING) return element()->getDataPointer(); else return NULL; } operator char () { char v; element()->get(v); return v; } operator unsigned char () { unsigned char v; element()->get(v); return v; } operator int () { int v; element()->get(v); return v; } operator unsigned int () { unsigned int v; element()->get(v); return v; } operator long () { long v; element()->get(v); return v; } operator unsigned long () { unsigned long v; element()->get(v); return v; } operator long long () { long long v; element()->get(v); return v; } operator float () { float v; element()->get(v); return v; } operator double () { double v; element()->get(v); return v; } operator long double () { long double v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } operator vector () { vector v; element()->get(v); return v; } const string &operator= (const string &s) { element()->set(s); return s; } const wstring &operator= (const wstring &s) { element()->set(s); return s; } char operator= (char i) { element()->set(i); return i; } unsigned char operator= (unsigned char i) { element()->set(i); return i; } int operator= (int i) { element()->set(i); return i; } unsigned int operator= (unsigned int i) { element()->set(i); return i; } long operator= (long i) { element()->set(i); return i; } unsigned long operator= (unsigned long i) { element()->set(i); return i; } long long operator= (long long i) { element()->set(i); return i; } float operator= (float i) { element()->set(i); return i; } double operator= (double i) { element()->set(i); return i; } long double operator= (long double i) { element()->set(i); return i; } vector &operator= (vector &v) { element()->set(v); return v; } vector &operator= (vector &v) { element()->set(v); return v; } vector &operator= (vector &v) { element()->set(v); return v; } vector &operator= (vector &v) { element()->set(v); return v; } vector &operator= (vector &v) { element()->set(v); return v; } vector &operator= (vector &v) { element()->set(v); return v; } vector &operator= (vector &v) { element()->set(v); return v; } vector &operator= (vector &v) { element()->set(v); return v; } vector &operator= (vector &v) { element()->set(v); return v; } DataNode *operator[] (const char *name_in) { return getNext(name_in); } DataNode *operator[] (int idx) { return child(idx); } bool operator() (const char *name_in) { return hasAnother(name_in); } bool operator() () { return hasAnother(); } DataNode *operator ^(const char *name_in) { return newChild(name_in); } }; typedef vector DataNodeList; enum DT_FloatingPointPolicy { USE_FLOAT, USE_DOUBLE }; class DataTree { private: DataNode dn_root; string wsEncode(const wstring& wstr); wstring wsDecode(const string& str); public: DataTree(const char *name_in); DataTree(); ~DataTree(); DataNode *rootNode(); void nodeToXML(DataNode *elem, TiXmlElement *elxml); void setFromXML(DataNode *elem, TiXmlNode *elxml, bool root_node=true, DT_FloatingPointPolicy fpp=USE_FLOAT); void decodeXMLText(DataNode *elem, const char *in_text, DT_FloatingPointPolicy fpp); void printXML(); /* print datatree as XML */ long getSerializedSize(DataElement &de_node_names, bool debug=false); /* get serialized size + return node names header */ long getSerialized(char **ser_str, bool debug=false); void setSerialized(char *ser_str, bool debug=false); bool LoadFromFileXML(const std::string& filename, DT_FloatingPointPolicy fpp=USE_FLOAT); bool SaveToFileXML(const std::string& filename); // bool SaveToFile(const std::string& filename); // bool LoadFromFile(const std::string& filename); bool SaveToFile(const std::string& filename, bool compress = true, int compress_level = 2); bool LoadFromFile(const std::string& filename); }; CubicSDR-0.2.3/src/util/GLExt.cpp000066400000000000000000000073621322677621400163670ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "GLExt.h" #include #include #ifdef __APPLE__ #include #endif #ifdef __linux__ #include #endif #ifdef _WIN32 PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT = NULL; PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL; bool GLExtSupported(const char *extension_name) { const GLubyte *extensions = glGetString(GL_EXTENSIONS); return (std::strstr((const char *)extensions, extension_name) != NULL); } #endif bool GLExt_initialized = false; void initGLExtensions() { if (GLExt_initialized) { return; } // const GLubyte *extensions = glGetString(GL_EXTENSIONS); // std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl; #ifdef __APPLE__ const GLint interval = 1; #else const GLint interval = 2; #endif #ifdef _WIN32 if (GLExtSupported("WGL_EXT_swap_control")) { std::cout << "Initializing WGL swap control extensions.." << std::endl; wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) wglGetProcAddress("wglGetSwapIntervalEXT"); wglSwapIntervalEXT(interval); } #endif #ifdef __APPLE__ // OSX is just ON / OFF CGLSetParameter (CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); #endif #ifdef __linux__ dlopen("libglx.so",RTLD_LAZY); void (*glxSwapIntervalEXTFunc) (Display *dpy, GLXDrawable drawable, int interval) = 0; int (*glxSwapIntervalMESAFunc)(unsigned int interval) = 0; int (*glxSwapIntervalSGIFunc) (int interval) = 0; void (*DRI2SwapIntervalFunc) (Display *dpy, XID drawable, int interval) = 0; glxSwapIntervalEXTFunc = (void (*) (Display *dpy, GLXDrawable drawable, int interval)) dlsym(RTLD_DEFAULT,"glXSwapIntervalEXT"); glxSwapIntervalMESAFunc = (int (*)(unsigned int interval)) dlsym(RTLD_DEFAULT,"glXSwapIntervalMESA"); glxSwapIntervalSGIFunc = (int (*) (int interval)) dlsym(RTLD_DEFAULT,"glXSwapIntervalSGI"); DRI2SwapIntervalFunc = (void (*) (Display *dpy, XID drawable, int interval)) dlsym(RTLD_DEFAULT,"DRI2SwapInterval"); std::cout << "Available vertical sync SwapInterval functions: " << std::endl; std::cout << "\tglxSwapIntervalEXT: " << ((glxSwapIntervalEXTFunc != 0)?"Yes":"No") << std::endl; std::cout << "\tDRI2SwapInterval: " << ((DRI2SwapIntervalFunc != 0)?"Yes":"No") << std::endl; std::cout << "\tglxSwapIntervalMESA: " << ((glxSwapIntervalMESAFunc != 0)?"Yes":"No") << std::endl; std::cout << "\tglxSwapIntervalSGI: " << ((glxSwapIntervalSGIFunc != 0)?"Yes":"No") << std::endl; if (glxSwapIntervalEXTFunc) { Display *dpy = glXGetCurrentDisplay(); GLXDrawable drawable = glXGetCurrentDrawable(); glxSwapIntervalEXTFunc(dpy, drawable, interval); std::cout << "Using glxSwapIntervalEXT." << std::endl << std::endl; } else if (DRI2SwapIntervalFunc) { Display *dpy = glXGetCurrentDisplay(); GLXDrawable drawable = glXGetCurrentDrawable(); DRI2SwapIntervalFunc(dpy, drawable, interval); std::cout << "Using DRI2SwapInterval." << std::endl << std::endl; } else if (glxSwapIntervalMESAFunc) { glxSwapIntervalMESAFunc(interval); std::cout << "Using glxSwapIntervalMESA." << std::endl << std::endl; } else if (glxSwapIntervalSGIFunc) { glxSwapIntervalSGIFunc(interval); std::cout << "Using glxSwapIntervalSGI." << std::endl << std::endl; } else { std::cout << "No vertical sync swap interval functions available." << std::endl; } #endif GLExt_initialized = true; } CubicSDR-0.2.3/src/util/GLExt.h000066400000000000000000000010221322677621400160170ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #ifdef _WIN32 #include #ifdef __MINGW32__ #include #else #include "wglext.h" #endif extern PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT; extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; extern PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; bool GLExtSupported(const char *extension_name); #endif extern bool GLExt_initialized; void initGLExtensions(); CubicSDR-0.2.3/src/util/GLFont.cpp000066400000000000000000000624711322677621400165370ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "GLFont.h" #include #include #include #include #include "cubic_math.h" #ifdef _OSX_APP_ #include "CoreFoundation/CoreFoundation.h" #endif #ifndef RES_FOLDER #define RES_FOLDER "" #endif #define GC_DRAW_COUNT_PERIOD 50 #define GC_DRAW_COUNT_LIMIT 10 GLFontStringCache::GLFontStringCache() { gc = 0; } //Static initialization of all available fonts, //using aggregate syntax (Cx11+) //Fonts must be listed in increasing size for Drawer to work ! GLFont GLFont::fonts[GLFont::GLFontSize::GLFONT_SIZE_MAX] = { { GLFont::GLFontSize::GLFONT_SIZE12, L"fonts/vera_sans_mono12.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE16, L"fonts/vera_sans_mono16.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE18, L"fonts/vera_sans_mono18.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE24, L"fonts/vera_sans_mono24.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE27, L"fonts/vera_sans_mono27.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE32, L"fonts/vera_sans_mono32.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE36, L"fonts/vera_sans_mono36.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE48, L"fonts/vera_sans_mono48.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE64, L"fonts/vera_sans_mono64.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE72, L"fonts/vera_sans_mono72.fnt" }, { GLFont::GLFontSize::GLFONT_SIZE96, L"fonts/vera_sans_mono96.fnt" }, }; std::atomic GLFont::currentScale{ GLFont::GLFontScale::GLFONT_SCALE_NORMAL }; GLFontChar::GLFontChar() : id(0), x(0), y(0), width(0), height(0), xoffset(0), yoffset(0), xadvance(0), aspect(1), index(0) { } GLFontChar::~GLFontChar() { } void GLFontChar::setId(int idval) { id = idval; } int GLFontChar::getId() { return id; } void GLFontChar::setXOffset(int xofs) { xoffset = xofs; } int GLFontChar::getXOffset() { return xoffset; } void GLFontChar::setYOffset(int yofs) { yoffset = yofs; } int GLFontChar::getYOffset() { return yoffset; } void GLFontChar::setX(int xpos) { x = xpos; } int GLFontChar::getX() { return x; } void GLFontChar::setY(int ypos) { y = ypos; } int GLFontChar::getY() { return y; } void GLFontChar::setWidth(int w) { width = w; if (width && height) { aspect = (float) width / (float) height; } } int GLFontChar::getWidth() { return width; } void GLFontChar::setHeight(int h) { height = h; if (width && height) { aspect = (float) width / (float) height; } } int GLFontChar::getHeight() { return height; } void GLFontChar::setXAdvance(int xadv) { xadvance = xadv; } int GLFontChar::getXAdvance() { return xadvance; } float GLFontChar::getAspect() { return aspect; } void GLFontChar::setIndex(unsigned int idx) { index = idx; } int GLFontChar::getIndex() { return index; } GLFont::GLFont(GLFontSize size, std::wstring defFileName): lineHeight(0), base(0), imageWidth(0), imageHeight(0), loaded(false), texId(0), gcCounter(0) { fontSizeClass = size; fontDefFileSource = defFileName; } GLFont::~GLFont() { } std::wstring GLFont::nextParam(std::wistringstream &str) { std::wstring param_str; str >> param_str; if (param_str.find(L'"') != std::wstring::npos) { std::wstring rest; while (!str.eof() && (std::count(param_str.begin(), param_str.end(), L'"') % 2)) { str >> rest; param_str.append(L" " + rest); } } return param_str; } std::wstring GLFont::getParamKey(const std::wstring& param_str) { std::wstring keyName; size_t eqpos = param_str.find(L"="); if (eqpos != std::wstring::npos) { keyName = param_str.substr(0, eqpos); } return keyName; } std::wstring GLFont::getParamValue(const std::wstring& param_str) { std::wstring value; size_t eqpos = param_str.find(L"="); if (eqpos != std::wstring::npos) { value = param_str.substr(eqpos + 1); } if (value[0] == L'"' && value[value.length() - 1] == L'"') { value = value.substr(1, value.length() - 2); } return value; } void GLFont::loadFontOnce() { if (loaded) { return; } #if _OSX_APP_ CFBundleRef mainBundle = CFBundleGetMainBundle(); CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); char path[PATH_MAX]; if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX)) { // error! } CFRelease(resourcesURL); wxString resourceFolder = std::string(path) + "/"; #else wxString resourceFolder = RES_FOLDER; #endif //full font file path wxFileName fontDefFileName = wxFileName(resourceFolder + L"/" + fontDefFileSource); if (!fontDefFileName.Exists()) { wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath()); //Full Path where the fonts are, including file name fontDefFileName = wxFileName(exePath.GetPath() + L"/"+ fontDefFileSource); if (!fontDefFileName.FileExists()) { std::cout << "Font file " << fontDefFileName.GetFullPath() << " does not exist?" << std::endl; return; } if (!fontDefFileName.IsFileReadable()) { std::cout << "Font file " << fontDefFileName.GetFullPath() << " is not readable?" << std::endl; return; } } else { if (!fontDefFileName.IsFileReadable()) { std::cout << "Font file " << fontDefFileName.GetFullPath() << " is not readable?" << std::endl; return; } } //Re-compute the resource dir. resourceFolder = fontDefFileName.GetPath(); std::string fontDefFileNamePath = fontDefFileName.GetFullPath(wxPATH_NATIVE).ToStdString(); std::wifstream input; input.open(fontDefFileNamePath, std::ios::in); std::wstring op; while (!input.eof()) { input >> op; if (op == L"info") { std::wstring info_param_str; getline(input, info_param_str); std::wistringstream info_param(info_param_str); while (!info_param.eof()) { std::wstring param = nextParam(info_param); std::wstring paramKey = getParamKey(param); if (paramKey == L"face") { fontName = getParamValue(param); } param = nextParam(info_param); paramKey = getParamKey(param); if (paramKey == L"size") { std::wistringstream paramValue(getParamValue(param)); paramValue >> pixHeight; } // std::cout << "[" << paramKey << "] = '" << paramValue << "'" << std::endl; } } else if (op == L"common") { std::wstring common_param_str; getline(input, common_param_str); std::wistringstream common_param(common_param_str); while (!common_param.eof()) { std::wstring param = nextParam(common_param); std::wstring paramKey = getParamKey(param); std::wistringstream paramValue(getParamValue(param)); if (paramKey == L"lineHeight") { paramValue >> lineHeight; } else if (paramKey == L"base") { paramValue >> base; } else if (paramKey == L"scaleW") { paramValue >> imageWidth; } else if (paramKey == L"scaleH") { paramValue >> imageHeight; } // std::cout << "[" << paramKey << "] = '" << getParamValue(param) << "'" << std::endl; } } else if (op == L"page") { std::wstring page_param_str; getline(input, page_param_str); std::wistringstream page_param(page_param_str); while (!page_param.eof()) { std::wstring param = nextParam(page_param); std::wstring paramKey = getParamKey(param); std::wstring paramValue = getParamValue(param); if (paramKey == L"file") { wxFileName imgFileName = wxFileName(resourceFolder, paramValue); imageFile = imgFileName.GetFullPath(wxPATH_NATIVE).ToStdWstring(); } // std::cout << "[" << paramKey << "] = '" << paramValue << "'" << std::endl; } } else if (op == L"char") { std::wstring char_param_str; getline(input, char_param_str); std::wistringstream char_param(char_param_str); GLFontChar *newChar = new GLFontChar; while (!char_param.eof()) { std::wstring param = nextParam(char_param); std::wstring paramKey = getParamKey(param); std::wistringstream paramValue(getParamValue(param)); int val; if (paramKey == L"id") { paramValue >> val; newChar->setId(val); } else if (paramKey == L"x") { paramValue >> val; newChar->setX(val); } else if (paramKey == L"y") { paramValue >> val; newChar->setY(val); } else if (paramKey == L"width") { paramValue >> val; newChar->setWidth(val); } else if (paramKey == L"height") { paramValue >> val; newChar->setHeight(val); } else if (paramKey == L"xoffset") { paramValue >> val; newChar->setXOffset(val); } else if (paramKey == L"yoffset") { paramValue >> val; newChar->setYOffset(val); } else if (paramKey == L"xadvance") { paramValue >> val; newChar->setXAdvance(val); } // std::cout << "[" << paramKey << "] = '" << getParamValue(param) << "'" << std::endl; } characters[newChar->getId()] = newChar; } else { std::wstring dummy; getline(input, dummy); } } if (imageFile != "" && imageWidth && imageHeight && characters.size()) { // Load file and decode image. std::vector image; unsigned int imgWidth = imageWidth, imgHeight = imageHeight; //1) First load the raw file to memory using wstring filenames wxFile png_file(imageFile); int png_size = png_file.Length(); unsigned char* raw_image = new unsigned char[png_size]; if (png_size > 0) { int nbRead = png_file.Read((void*)raw_image, png_size); if (png_size != nbRead) { std::cout << "Error loading the full PNG image file in memory: '" << imageFile << "'" << std::endl; } } //2) then load from memory unsigned error = lodepng::decode(image, imgWidth, imgHeight, raw_image, png_size); delete[] raw_image; png_file.Close(); if (error) { std::cout << "Error decoding PNG image file: '" << imageFile << "'" << std::endl; } glGenTextures(1, &texId); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texId); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, 4, imageWidth, imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, error?nullptr:(&image[0])); glDisable(GL_TEXTURE_2D); std::map::iterator char_i; gl_vertices.resize(characters.size() * 8); // one quad per char gl_uv.resize(characters.size() * 8); unsigned int ofs = 0; for (char_i = characters.begin(); char_i != characters.end(); char_i++) { // int charId = (*char_i).first; GLFontChar *fchar = (*char_i).second; float faspect = fchar->getAspect(); float uv_xpos = (float) fchar->getX() / (float) imageWidth; float uv_ypos = ((float) fchar->getY() / (float) imageHeight); float uv_xofs = (float) fchar->getWidth() / (float) imageWidth; float uv_yofs = ((float) fchar->getHeight() / (float) imageHeight); gl_vertices[ofs] = 0; gl_vertices[ofs + 1] = 0; gl_uv[ofs] = uv_xpos; gl_uv[ofs + 1] = uv_ypos + uv_yofs; gl_vertices[ofs + 2] = faspect; gl_vertices[ofs + 3] = 0; gl_uv[ofs + 2] = uv_xpos + uv_xofs; gl_uv[ofs + 3] = uv_ypos + uv_yofs; gl_vertices[ofs + 4] = faspect; gl_vertices[ofs + 5] = 1; gl_uv[ofs + 4] = uv_xpos + uv_xofs; gl_uv[ofs + 5] = uv_ypos; gl_vertices[ofs + 6] = 0; gl_vertices[ofs + 7] = 1; gl_uv[ofs + 6] = uv_xpos; gl_uv[ofs + 7] = uv_ypos; fchar->setIndex(ofs); ofs += 8; } std::cout << "Loaded font '" << fontName << "' from '" << imageFile << "', parsed " << characters.size() << " characters." << std::endl; loaded = true; } else { std::cout << "Error loading font file " << imageFile << std::endl; } input.close(); loaded = true; } float GLFont::getStringWidth(const std::wstring& str, float size, float viewAspect) { float scalex = size / viewAspect; float width = 0; for (int i = 0, iMax = str.length(); i < iMax; i++) { int charId = str.at(i); if (characters.find(charId) == characters.end()) { continue; } GLFontChar *fchar = characters[charId]; float ofsx = (float) fchar->getXOffset() / (float) imageWidth; float advx = (float) fchar->getXAdvance() / (float) imageWidth; if (charId == 32) { advx = characters[L'_']->getAspect(); } width += fchar->getAspect() + advx + ofsx; } width *= scalex; return width; } // Draw string, immediate void GLFont::drawString(const std::wstring& str, int pxHeight, float xpos, float ypos, Align hAlign, Align vAlign, int vpx, int vpy, bool cacheable) { pxHeight *= 2; if (!vpx || !vpy) { GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); vpx = vp[2]; vpy = vp[3]; } if (cacheable) { gcCounter++; std::lock_guard lock(cache_busy); if (gcCounter > GC_DRAW_COUNT_PERIOD) { doCacheGC(); gcCounter = 0; } GLFontStringCache *fc = nullptr; std::map::iterator cache_iter; std::wstringstream sscacheIdx; sscacheIdx << vpx << "." << vpy << "." << pxHeight << "." << str; std::wstring cacheIdx(sscacheIdx.str()); cache_iter = stringCache.find(cacheIdx); if (cache_iter != stringCache.end()) { fc = cache_iter->second; fc->gc = 0; } if (fc == nullptr) { // std::cout << "cache miss" << std::endl; fc = cacheString(str, pxHeight, vpx, vpy); stringCache[cacheIdx] = fc; } drawCacheString(fc, xpos, ypos, hAlign, vAlign); return; } float size = (float) pxHeight / (float) vpy; float viewAspect = (float) vpx / (float) vpy; float msgWidth = getStringWidth(str, size, viewAspect); glPushMatrix(); glTranslatef(xpos, ypos, 0.0f); switch (vAlign) { case GLFONT_ALIGN_TOP: glTranslatef(0.0, -size, 0.0); break; case GLFONT_ALIGN_CENTER: glTranslatef(0.0, -size/2.0, 0.0); break; default: break; } switch (hAlign) { case GLFONT_ALIGN_RIGHT: glTranslatef(-msgWidth, 0.0, 0.0); break; case GLFONT_ALIGN_CENTER: glTranslatef(-msgWidth / 2.0, 0.0, 0.0); break; default: break; } glPushMatrix(); glScalef(size / viewAspect, size, 1.0f); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texId); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &gl_vertices[0]); glTexCoordPointer(2, GL_FLOAT, 0, &gl_uv[0]); for (int i = 0, iMax = str.length(); i < iMax; i++) { int charId = str.at(i); if (characters.find(charId) == characters.end()) { continue; } GLFontChar *fchar = characters[charId]; float ofsx = (float) fchar->getXOffset() / (float) imageWidth; float advx = (float) fchar->getXAdvance() / (float) imageWidth; if (charId == 32) { advx = characters[L'_']->getAspect(); } glTranslatef(ofsx, 0.0, 0.0); glDrawArrays(GL_QUADS, fchar->getIndex() / 2, 4); glTranslatef(fchar->getAspect() + advx, 0.0, 0.0); } glVertexPointer(2, GL_FLOAT, 0, nullptr); glTexCoordPointer(2, GL_FLOAT, 0, nullptr); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glPopMatrix(); glPopMatrix(); glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); } // Draw string, immediate, 8 bit version void GLFont::drawString(const std::string& str, int pxHeight, float xpos, float ypos, Align hAlign, Align vAlign, int vpx, int vpy, bool cacheable) { //Displayed string is wstring, so use wxString to do the heavy lifting of converting str... wxString wsTmp; wsTmp.assign(str); drawString(wsTmp.ToStdWstring(), pxHeight, xpos, ypos, hAlign, vAlign, vpx, vpy, cacheable); } // Draw cached GLFontCacheString void GLFont::drawCacheString(GLFontStringCache *fc, float xpos, float ypos, Align hAlign, Align vAlign) { float size = (float) fc->pxHeight / (float) fc->vpy; glPushMatrix(); glTranslatef(xpos, ypos, 0.0f); switch (vAlign) { case GLFONT_ALIGN_TOP: glTranslatef(0.0, -size, 0.0); break; case GLFONT_ALIGN_CENTER: glTranslatef(0.0, -size/2.0, 0.0); break; default: break; } switch (hAlign) { case GLFONT_ALIGN_RIGHT: glTranslatef(-fc->msgWidth, 0.0, 0.0); break; case GLFONT_ALIGN_CENTER: glTranslatef(-fc->msgWidth / 2.0, 0.0, 0.0); break; default: break; } glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texId); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &fc->gl_vertices[0]); glTexCoordPointer(2, GL_FLOAT, 0, &fc->gl_uv[0]); glDrawArrays(GL_QUADS, 0, 4 * fc->drawlen); glVertexPointer(2, GL_FLOAT, 0, nullptr); glTexCoordPointer(2, GL_FLOAT, 0, nullptr); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glPopMatrix(); glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); } // Compile optimized GLFontCacheString GLFontStringCache *GLFont::cacheString(const std::wstring& str, int pxHeight, int vpx, int vpy) { GLFontStringCache *fc = new GLFontStringCache; fc->pxHeight = pxHeight; fc->vpx = vpx; fc->vpy = vpy; float size = (float) pxHeight / (float) vpy; float viewAspect = (float) vpx / (float) vpy; fc->msgWidth = getStringWidth(str, size, viewAspect); int nChar = 0; for (int i = 0, iMax = str.length(); i < iMax; i++) { int charId = str.at(i); if (characters.find(charId) == characters.end()) { continue; } nChar++; } fc->drawlen = nChar; fc->gl_vertices.resize(nChar*8); fc->gl_uv.resize(nChar*8); CubicVR::mat4 trans = CubicVR::mat4::scale(size / viewAspect, size, 1.0f); int c = 0; for (int i = 0, iMax = str.length(); i < iMax; i++) { int charId = str.at(i); if (characters.find(charId) == characters.end()) { continue; } GLFontChar *fchar = characters[charId]; float ofsx = (float) fchar->getXOffset() / (float) imageWidth; float advx = (float) fchar->getXAdvance() / (float) imageWidth; if (charId == 32) { advx = characters[L'_']->getAspect(); } // freeze transform to buffer trans *= CubicVR::mat4::translate(ofsx, 0.0, 0.0); int charIdx = fchar->getIndex(); for (int j = 0; j < 8; j+=2) { CubicVR::vec3 pt(gl_vertices[charIdx + j],gl_vertices[charIdx + j + 1], 0.0); pt = CubicVR::mat4::multiply(trans, pt, true); fc->gl_vertices[c * 8 + j] = pt[0]; fc->gl_vertices[c * 8 + j + 1] = pt[1]; fc->gl_uv[c * 8 + j] = gl_uv[charIdx + j]; fc->gl_uv[c * 8 + j + 1] = gl_uv[charIdx + j + 1]; } trans *= CubicVR::mat4::translate(fchar->getAspect() + advx, 0.0, 0.0); c++; } return fc; } void GLFont::doCacheGC() { std::map::iterator cache_iter; bool flushDone = false; //do aging and remove in one pass. cache_iter = stringCache.begin(); while (cache_iter != stringCache.end()) { //aging cache_iter->second->gc--; //only flush 1 element per call if (!flushDone && cache_iter->second->gc < -GC_DRAW_COUNT_LIMIT) { delete cache_iter->second; cache_iter = stringCache.erase(cache_iter); flushDone = true; } else { cache_iter++; } } //end while } void GLFont::clearCache() { std::lock_guard lock(cache_busy); std::map::iterator cache_iter; cache_iter = stringCache.begin(); while (cache_iter != stringCache.end()) { delete cache_iter->second; cache_iter = stringCache.erase(cache_iter); } } void GLFont::clearAllCaches() { for (int i = 0; i < GLFont::GLFONT_SIZE_MAX; i++) { fonts[i].clearCache(); } } GLFont::Drawer GLFont::getFont(int requestedSize, double scaleFactor) { return GLFont::Drawer(requestedSize, scaleFactor); } void GLFont::setScale(GLFontScale scale) { //safety vs. inputs if (scale < GLFONT_SCALE_NORMAL || scale > GLFONT_SCALE_LARGE) { scale = GLFontScale::GLFONT_SCALE_NORMAL; } currentScale.store(scale); //Flush all the GC stuff clearAllCaches(); } GLFont::GLFontScale GLFont::getScale() { return currentScale.load(); } double GLFont::getScaleFactor() { GLFontScale scale = currentScale.load(); if (scale == GLFONT_SCALE_MEDIUM) { return 1.5; } else if (scale == GLFONT_SCALE_LARGE) { return 2.0; } return 1.0; } int GLFont::getScaledPx(int basicFontSize, double scaleFactor) { //try to align on an integer pixel size if the targetSize font is available int targetSize = round(basicFontSize * scaleFactor); int resultIndex = 0; fonts[0].loadFontOnce(); for (int i = 0; i < GLFONT_SIZE_MAX - 1; i++) { fonts[i + 1].loadFontOnce(); if (fonts[i + 1].pixHeight <= targetSize) { resultIndex = i + 1; } else { break; } } //end for // return font height px return fonts[resultIndex].pixHeight; } GLFont::Drawer::Drawer(int basicFontSize, double scaleFactor) { //Selection of the final font: scan GLFont::fonts to find the biggest font such as // its pixHeight <= basicFontSize * scaleFactor. //then compute finalScaleFactor the zooming factor of renderingFont to reach a //final font size of basicFontSize* scaleFactor: renderingFontIndex = 0; //try to align on an integer pixel size if the targetSize font is available int targetSize = round(basicFontSize * scaleFactor); fonts[0].loadFontOnce(); for (int i = 0; i < GLFONT_SIZE_MAX - 1; i++) { fonts[i + 1].loadFontOnce(); if (fonts[i + 1].pixHeight <= targetSize) { renderingFontIndex = i + 1; } else { break; } } //end for // int rawSize = fonts[renderingFontIndex].pixHeight; //targetSize may not be reached yet, so the effective rendering font: fonts[renderingFontIndex] must be scaled up a bit. renderingFontScaleFactor = (double) targetSize / rawSize; } void GLFont::Drawer::drawString(const std::wstring& str, float xpos, float ypos, Align hAlign, Align vAlign, int vpx, int vpy, bool cacheable) { GLFont& appliedFont = fonts[renderingFontIndex]; appliedFont.drawString(str, round(appliedFont.pixHeight * renderingFontScaleFactor), xpos, ypos, hAlign, vAlign, vpx, vpy, cacheable); } //Public drawing font, 8 bit char version. void GLFont::Drawer::drawString(const std::string& str, float xpos, float ypos, Align hAlign, Align vAlign, int vpx, int vpy, bool cacheable) { GLFont& appliedFont = fonts[renderingFontIndex]; appliedFont.drawString(str, round(appliedFont.pixHeight * renderingFontScaleFactor), xpos, ypos, hAlign, vAlign, vpx, vpy, cacheable); } CubicSDR-0.2.3/src/util/GLFont.h000066400000000000000000000127121322677621400161750ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include #include "lodepng.h" #include "wx/glcanvas.h" #include "wx/filename.h" #include "wx/stdpaths.h" class GLFontStringCache { public: GLFontStringCache(); int drawlen; int vpx, vpy; int pxHeight = 0; float msgWidth = 0.0f; std::atomic_int gc; std::vector gl_vertices; std::vector gl_uv; }; class GLFontChar { public: GLFontChar(); ~GLFontChar(); void setId(int idval); // Returns the code point of the 16bit character, supposely Unicode. int getId(); void setXOffset(int xofs); int getXOffset(); void setYOffset(int yofs); int getYOffset(); void setX(int xpos); int getX(); void setY(int ypos); int getY(); void setWidth(int w); int getWidth(); void setHeight(int h); int getHeight(); void setXAdvance(int xadv); int getXAdvance(); float getAspect(); void setIndex(unsigned int idx); int getIndex(); private: // this is the code point of the 16bit character, supposly Unicode. int id; int x, y, width, height; int xoffset, yoffset; int xadvance; float aspect; int index; }; class GLFont { public: enum Align { GLFONT_ALIGN_LEFT, GLFONT_ALIGN_RIGHT, GLFONT_ALIGN_CENTER, GLFONT_ALIGN_TOP, GLFONT_ALIGN_BOTTOM }; enum GLFontSize { GLFONT_SIZE12, GLFONT_SIZE16, GLFONT_SIZE18, GLFONT_SIZE24, GLFONT_SIZE27, //new GLFONT_SIZE32, GLFONT_SIZE36, //new GLFONT_SIZE48, GLFONT_SIZE64, //new GLFONT_SIZE72, //new GLFONT_SIZE96, //new GLFONT_SIZE_MAX }; enum GLFontScale { GLFONT_SCALE_NORMAL, GLFONT_SCALE_MEDIUM, // x1.5 GLFONT_SCALE_LARGE, // x2 GLFONT_SCALE_MAX }; GLFont(GLFontSize size, std::wstring fontFileName); ~GLFont(); //Called to change the scale of the rendered fonts static void setScale(GLFontScale scale); static GLFontScale getScale(); //Mean current scale factor: 1.0 in normal, 1.5 medium, 2.0 for large static double getScaleFactor(); //Return a valid font px height given the font size and scale factor static int getScaledPx(int basicFontSize, double scaleFactor); private: std::wstring nextParam(std::wistringstream &str); std::wstring getParamKey(const std::wstring& param_str); std::wstring getParamValue(const std::wstring& param_str); //Repository of all loaded fonts static GLFont fonts[GLFontSize::GLFONT_SIZE_MAX]; static std::atomic currentScale; //load a given font file, (lazy loading) void loadFontOnce(); //private drawing font, 16 bit char version, called by Drawer object void drawString(const std::wstring& str, int pxHeight, float xpos, float ypos, Align hAlign = GLFONT_ALIGN_LEFT, Align vAlign = GLFONT_ALIGN_TOP, int vpx = 0, int vpy = 0, bool cacheable = false); //private drawing font, 8 bit char version, called by Drawer object void drawString(const std::string& str, int pxHeight, float xpos, float ypos, Align hAlign = GLFONT_ALIGN_LEFT, Align vAlign = GLFONT_ALIGN_TOP, int vpx = 0, int vpy = 0, bool cacheable = false); GLFontStringCache *cacheString(const std::wstring& str, int pxHeight, int vpx, int vpy); void drawCacheString(GLFontStringCache *fc, float xpos, float ypos, Align hAlign, Align vAlign); void doCacheGC(); void clearCache(); //force GC of all available fonts static void clearAllCaches(); float getStringWidth(const std::wstring& str, float size, float viewAspect); //the string cache is per-front (internal font) std::map stringCache; int lineHeight; int base; int imageWidth, imageHeight, pixHeight; bool loaded; GLFontSize fontSizeClass; std::map characters; std::vector gl_vertices; std::vector gl_uv; //The font name as written in the def file. std::wstring fontName; //The full path font PNG filename std::wstring imageFile; //the real path location of the font definition file std::wstring fontDefFileSource; GLuint texId; int gcCounter; std::mutex cache_busy; public: //Proxy class computing and caching the selection of the underlying fonts //depending of the user input and requested scale for the fonts. class Drawer { private: //result of the computation int renderingFontIndex = 0; double renderingFontScaleFactor = 1.0; public: Drawer(int basicFontSize, double scaleFactor); //Public drawing font, 16 bit char version. void drawString(const std::wstring& str, float xpos, float ypos, Align hAlign = GLFONT_ALIGN_LEFT, Align vAlign = GLFONT_ALIGN_TOP, int vpx = 0, int vpy = 0, bool cacheable = false); //Public drawing font, 8 bit char version. void drawString(const std::string& str, float xpos, float ypos, Align hAlign = GLFONT_ALIGN_LEFT, Align vAlign = GLFONT_ALIGN_TOP, int vpx = 0, int vpy = 0, bool cacheable = false); }; //end class Drawer //The User request a font of size requestedSize to display, with an additional //optional scale factor scaleFactor. static GLFont::Drawer getFont(int requestedSize, double scaleFactor = 1.0); }; CubicSDR-0.2.3/src/util/Gradient.cpp000066400000000000000000000032361322677621400171350ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "Gradient.h" #include Gradient::Gradient() { } void Gradient::addColor(GradientColor c) { colors.push_back(c); } std::vector &Gradient::getRed() { return r_val; } std::vector &Gradient::getGreen() { return g_val; } std::vector &Gradient::getBlue() { return b_val; } void Gradient::generate(unsigned int len) { size_t chunk_size = len / (colors.size() - 1); size_t p = 0; r_val.resize(len); g_val.resize(len); b_val.resize(len); for (size_t j = 0, jMax = colors.size() - 1; j < jMax; j++) { if ((chunk_size * (jMax)) < len && (j == jMax - 1)) { chunk_size += len - chunk_size * (jMax); } for (size_t i = 0; i < chunk_size; i++) { float idx = (float) (i) / (float) chunk_size; float r1 = colors[j].r; float g1 = colors[j].g; float b1 = colors[j].b; float r2 = colors[j + 1].r; float g2 = colors[j + 1].g; float b2 = colors[j + 1].b; float r = r1 + (r2 - r1) * idx; float g = g1 + (g2 - g1) * idx; float b = b1 + (b2 - b1) * idx; if (r < 0.0) r = 0.0; if (r > 1.0) r = 1.0; if (g < 0.0) g = 0.0; if (g > 1.0) g = 1.0; if (b < 0.0) b = 0.0; if (b > 1.0) b = 1.0; r_val[p] = r; g_val[p] = g; b_val[p] = b; p++; } } } Gradient::~Gradient() { } CubicSDR-0.2.3/src/util/Gradient.h000066400000000000000000000012261322677621400165770ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include class GradientColor { public: float r, g, b; float w; GradientColor(float r_in, float g_in, float b_in) : r(r_in), g(g_in), b(b_in), w(1) { } }; class Gradient { public: Gradient(); void addColor(GradientColor c); std::vector &getRed(); std::vector &getGreen(); std::vector &getBlue(); void generate(unsigned int len); ~Gradient(); private: std::vector colors; std::vector r_val; std::vector g_val; std::vector b_val; }; CubicSDR-0.2.3/src/util/MouseTracker.cpp000066400000000000000000000104171322677621400200030ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "MouseTracker.h" #include MouseTracker::MouseTracker(wxWindow *target) : mouseX(0), mouseY(0), lastMouseX(0), lastMouseY(0), originMouseX(0), originMouseY(0), deltaMouseX(0), deltaMouseY(0), vertDragLock(false), horizDragLock( false), isMouseDown(false), isMouseRightDown(false), isMouseInView(false), target(target) { } MouseTracker::MouseTracker() : MouseTracker(NULL) { } void MouseTracker::OnMouseMoved(wxMouseEvent& event) { if (target == NULL) { return; } const wxSize ClientSize = target->GetClientSize(); mouseX = (float) event.m_x / (float) ClientSize.x; mouseY = 1.0 - (float) event.m_y / (float) ClientSize.y; deltaMouseX = mouseX - lastMouseX; deltaMouseY = mouseY - lastMouseY; if (isMouseDown || isMouseRightDown) { #ifndef __APPLE__ #ifndef __linux__ if (horizDragLock && vertDragLock) { target->WarpPointer(originMouseX * ClientSize.x, (1.0 - originMouseY) * ClientSize.y); mouseX = originMouseX; mouseY = originMouseY; } else if (vertDragLock && mouseY != lastMouseY) { target->WarpPointer(event.m_x, (1.0 - originMouseY) * ClientSize.y); mouseY = originMouseY; } else if (horizDragLock && mouseX != lastMouseX) { target->WarpPointer(originMouseX * ClientSize.x, event.m_y); mouseX = originMouseX; } #endif #endif } lastMouseX = mouseX; lastMouseY = mouseY; } void MouseTracker::OnMouseWheelMoved(wxMouseEvent& /* event */) { // std::cout << "wheel?" << std::endl; } void MouseTracker::OnMouseDown(wxMouseEvent& event) { if (isMouseRightDown || target == NULL) { return; } const wxSize ClientSize = target->GetClientSize(); mouseX = lastMouseX = (float) event.m_x / (float) ClientSize.x; mouseY = lastMouseY = 1.0 - (float) event.m_y / (float) ClientSize.y; originMouseX = mouseX; originMouseY = mouseY; isMouseDown = true; } void MouseTracker::OnMouseReleased(wxMouseEvent& /* event */) { isMouseDown = false; } void MouseTracker::OnMouseRightDown(wxMouseEvent& event) { if (isMouseDown || target == NULL) { return; } const wxSize ClientSize = target->GetClientSize(); mouseX = lastMouseX = (float) event.m_x / (float) ClientSize.x; mouseY = lastMouseY = 1.0 - (float) event.m_y / (float) ClientSize.y; originMouseX = mouseX; originMouseY = mouseY; isMouseRightDown = true; } void MouseTracker::OnMouseRightReleased(wxMouseEvent& /* event */) { isMouseRightDown = false; } void MouseTracker::OnMouseEnterWindow(wxMouseEvent& /* event */) { isMouseInView = true; isMouseDown = false; isMouseRightDown = false; } void MouseTracker::OnMouseLeftWindow(wxMouseEvent& /* event */) { isMouseDown = false; isMouseRightDown = false; isMouseInView = false; } float MouseTracker::getOriginMouseX() { return originMouseX; } float MouseTracker::getOriginMouseY() { return originMouseY; } float MouseTracker::getOriginDeltaMouseX() { return mouseX - originMouseX; } float MouseTracker::getOriginDeltaMouseY() { return mouseY - originMouseY; } float MouseTracker::getDeltaMouseX() { return deltaMouseX; } float MouseTracker::getDeltaMouseY() { return deltaMouseY; } float MouseTracker::getLastMouseX() { return lastMouseX; } float MouseTracker::getLastMouseY() { return lastMouseY; } CubicVR::vec2 MouseTracker::getGLXY() { return CubicVR::vec2((getMouseX()-0.5)*2.0, (getMouseY()-0.5)*2.0); } float MouseTracker::getMouseX() { return mouseX; } float MouseTracker::getMouseY() { return mouseY; } void MouseTracker::setVertDragLock(bool dragLock) { vertDragLock = dragLock; } void MouseTracker::setHorizDragLock(bool dragLock) { horizDragLock = dragLock; } bool MouseTracker::getVertDragLock() { return vertDragLock; } bool MouseTracker::getHorizDragLock() { return horizDragLock; } bool MouseTracker::mouseDown() { return isMouseDown; } bool MouseTracker::mouseInView() { return isMouseInView; } void MouseTracker::setTarget(wxWindow *target_in) { target = target_in; } bool MouseTracker::mouseRightDown() { return isMouseRightDown; } CubicSDR-0.2.3/src/util/MouseTracker.h000066400000000000000000000026311322677621400174470ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/window.h" #include "cubic_math.h" class MouseTracker { public: MouseTracker(wxWindow *target); MouseTracker(); void OnMouseMoved(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseRightDown(wxMouseEvent& event); void OnMouseRightReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); float getOriginMouseX(); float getOriginMouseY(); float getOriginDeltaMouseX(); float getOriginDeltaMouseY(); float getDeltaMouseX(); float getDeltaMouseY(); float getLastMouseX(); float getLastMouseY(); CubicVR::vec2 getGLXY(); float getMouseX(); float getMouseY(); void setVertDragLock(bool dragLock); void setHorizDragLock(bool dragLock); bool getVertDragLock(); bool getHorizDragLock(); bool mouseDown(); bool mouseRightDown(); bool mouseInView(); void setTarget(wxWindow *target_in); private: float mouseX, mouseY; float lastMouseX, lastMouseY; float originMouseX, originMouseY; float deltaMouseX, deltaMouseY; bool vertDragLock, horizDragLock; bool isMouseDown, isMouseRightDown, isMouseInView; wxWindow *target; }; CubicSDR-0.2.3/src/util/ThreadBlockingQueue.cpp000066400000000000000000000001511322677621400212560ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include CubicSDR-0.2.3/src/util/ThreadBlockingQueue.h000066400000000000000000000170061322677621400207320ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include #include #include #include #include #define MIN_ITEM_NB (1) //use this timeout constant in either pop() or push() calls to indicate // a non-blocking operation, so respectively equivalent to try_pop() and try_push() #define NON_BLOCKING_TIMEOUT (100) //use this timeout constant in either pop() or push() calls to indicate //an indefnite timeout duration. #define BLOCKING_INFINITE_TIMEOUT (0) class ThreadQueueBase { }; typedef std::shared_ptr ThreadQueueBasePtr; /** A thread-safe asynchronous blocking queue */ template class ThreadBlockingQueue : public ThreadQueueBase { typedef typename std::deque::value_type value_type; typedef typename std::deque::size_type size_type; public: /*! Create safe blocking queue. */ ThreadBlockingQueue() { //at least 1 (== Java SynchronizedQueue) m_max_num_items = MIN_ITEM_NB; }; //Forbid Copy construction. ThreadBlockingQueue(const ThreadBlockingQueue& sq) = delete; /*! Forbid copy assignment. */ ThreadBlockingQueue& operator=(const ThreadBlockingQueue& sq) = delete; /*! Destroy safe queue. */ ~ThreadBlockingQueue() { std::lock_guard < std::mutex > lock(m_mutex); } /** * Sets the maximum number of items in the queue. Real value is clamped * to 1 on the lower bound. * \param[in] nb max of items */ void set_max_num_items(unsigned int max_num_items) { std::lock_guard < std::mutex > lock(m_mutex); if (max_num_items > m_max_num_items) { //Only raise the existing max size, never reduce it //for simplification sake at runtime. m_max_num_items = max_num_items; m_cond_not_full.notify_all(); } } /** * Pushes the item into the queue. If the queue is full, waits until room * is available, for at most timeout microseconds. * \param[in] item An item. * \param[in] timeout a max waiting timeout in microseconds for an item to be pushed. * by default, = 0 means indefinite wait. * \param[in] errorMessage if != nullptr (is nullptr by default) an error message written on std::cout in case of the timeout wait * \return true if an item was pushed into the queue, else a timeout has occured. */ bool push(const value_type& item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT,const char* errorMessage = nullptr) { std::unique_lock < std::mutex > lock(m_mutex); if (timeout == BLOCKING_INFINITE_TIMEOUT) { m_cond_not_full.wait(lock, [this]() // Lambda funct { return m_queue.size() < m_max_num_items; }); } else if (timeout <= NON_BLOCKING_TIMEOUT && m_queue.size() >= m_max_num_items) { // if the value is below a threshold, consider it is a try_push() return false; } else if (false == m_cond_not_full.wait_for(lock, std::chrono::microseconds(timeout), [this]() { return m_queue.size() < m_max_num_items; })) { if (errorMessage != nullptr) { std::thread::id currentThreadId = std::this_thread::get_id(); std::cout << "WARNING: Thread 0x" << std::hex << currentThreadId << std::dec << " (" << currentThreadId << ") executing {" << typeid(*this).name() << "}.push() has failed with timeout > " << (timeout * 0.001) << " ms, message: '" << errorMessage << "'" << std::endl << std::flush; } return false; } m_queue.push_back(item); m_cond_not_empty.notify_all(); return true; } /** * Try to pushes the item into the queue, immediatly, without waiting. If the queue is full, the item * is not inserted and the function returns false. * \param[in] item An item. */ bool try_push(const value_type& item) { std::lock_guard < std::mutex > lock(m_mutex); if (m_queue.size() >= m_max_num_items) { return false; } m_queue.push_back(item); m_cond_not_empty.notify_all(); return true; } /** * Pops item from the queue. If the queue is empty, blocks for timeout microseconds, or until item becomes available. * \param[in] timeout The number of microseconds to wait. O (default) means indefinite wait. * \param[in] errorMessage if != nullptr (is nullptr by default) an error message written on std::cout in case of the timeout wait * \return true if get an item from the queue, false if no item is received before the timeout. */ bool pop(value_type& item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT, const char* errorMessage = nullptr) { std::unique_lock < std::mutex > lock(m_mutex); if (timeout == BLOCKING_INFINITE_TIMEOUT) { m_cond_not_empty.wait(lock, [this]() // Lambda funct { return !m_queue.empty(); }); } else if (timeout <= NON_BLOCKING_TIMEOUT && m_queue.empty()) { // if the value is below a threshold, consider it is try_pop() return false; } else if (false == m_cond_not_empty.wait_for(lock, std::chrono::microseconds(timeout), [this]() { return !m_queue.empty(); })) { if (errorMessage != nullptr) { std::thread::id currentThreadId = std::this_thread::get_id(); std::cout << "WARNING: Thread 0x" << std::hex << currentThreadId << std::dec << " (" << currentThreadId << ") executing {" << typeid(*this).name() << "}.pop() has failed with timeout > " << (timeout * 0.001) << " ms, message: '" << errorMessage << "'" << std::endl << std::flush; } return false; } item = m_queue.front(); m_queue.pop_front(); m_cond_not_full.notify_all(); return true; } /** * Tries to pop item from the queue. * \param[out] item The item. * \return False is returned if no item is available. */ bool try_pop(value_type& item) { std::lock_guard < std::mutex > lock(m_mutex); if (m_queue.empty()) { return false; } item = m_queue.front(); m_queue.pop_front(); m_cond_not_full.notify_all(); return true; } /** * Gets the number of items in the queue. * \return Number of items in the queue. */ size_type size() const { std::lock_guard < std::mutex > lock(m_mutex); return m_queue.size(); } /** * Check if the queue is empty. * \return true if queue is empty. */ bool empty() const { std::lock_guard < std::mutex > lock(m_mutex); return m_queue.empty(); } /** * Check if the queue is full. * \return true if queue is full. */ bool full() const { std::lock_guard < std::mutex > lock(m_mutex); return (m_queue.size() >= m_max_num_items); } /** * Remove any items in the queue. */ void flush() { std::lock_guard < std::mutex > lock(m_mutex); m_queue.clear(); m_cond_not_full.notify_all(); } private: std::deque m_queue; mutable std::mutex m_mutex; std::condition_variable m_cond_not_empty; std::condition_variable m_cond_not_full; size_t m_max_num_items = MIN_ITEM_NB; }; CubicSDR-0.2.3/src/util/Timer.cpp000066400000000000000000000067261322677621400164670ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "Timer.h" #ifdef _WIN32 #include #endif #include Timer::Timer(void) : time_elapsed(0), system_milliseconds(0), start_time(0), end_time(0), last_update(0), num_updates(0), paused_time(0), offset(0), paused_state(false), lock_state(false), lock_rate(0) { #ifdef _WIN32 // According to Microsoft, QueryPerformanceXXX API is perfectly //fine for Windows 7+ systems, and use the highest appropriate counter. //this only need to be done once. ::QueryPerformanceFrequency(&win_frequency); #endif } void Timer::start(void) { update(); num_updates = 0; start_time = system_milliseconds; last_update = start_time; paused_state = false; lock_state = false; lock_rate = 0; paused_time = 0; offset = 0; } void Timer::stop(void) { end_time = system_milliseconds; } void Timer::reset(void) { start(); } void Timer::lockFramerate(float f_rate) { lock_rate = 1.0f/f_rate; lock_state = true; } void Timer::unlock() { unsigned long msec_tmp = system_milliseconds; lock_state = false; update(); last_update = system_milliseconds-(unsigned long)lock_rate; offset += msec_tmp-system_milliseconds; lock_rate = 0; } bool Timer::locked() { return lock_state; } void Timer::update(void) { num_updates++; last_update = system_milliseconds; if (lock_state) { system_milliseconds += (unsigned long)(lock_rate*1000.0); } else { #ifdef _WIN32 //Use QuaryPerformanceCounter, imune to problems sometimes //multimedia timers have. LARGE_INTEGER win_current_count; ::QueryPerformanceCounter(&win_current_count); system_milliseconds = (unsigned long)(win_current_count.QuadPart * 1000.0 / win_frequency.QuadPart); #else gettimeofday(&time_val,&time_zone); system_milliseconds = (unsigned long)time_val.tv_usec; system_milliseconds /= 1000; system_milliseconds += (unsigned long)(time_val.tv_sec*1000); #endif } if (paused_state) paused_time += system_milliseconds-last_update; time_elapsed = system_milliseconds-start_time-paused_time+offset; } unsigned long Timer::getMilliseconds(void) { return time_elapsed; } double Timer::getSeconds(void) { return ((double)getMilliseconds())/1000.0; } void Timer::setMilliseconds(unsigned long milliseconds_in) { offset -= (system_milliseconds-start_time-paused_time+offset)-milliseconds_in; } void Timer::setSeconds(double seconds_in) { setMilliseconds((long)(seconds_in*1000.0)); } double Timer::lastUpdateSeconds(void) { return ((double)lastUpdateMilliseconds())/1000.0; } unsigned long Timer::lastUpdateMilliseconds(void) { return system_milliseconds-last_update; } unsigned long Timer::totalMilliseconds() { return system_milliseconds-start_time; } double Timer::totalSeconds(void) { return totalMilliseconds()/1000.0; } unsigned long Timer::getNumUpdates(void) { return num_updates; } void Timer::paused(bool pause_in) { paused_state = pause_in; } bool Timer::paused() { return paused_state; } void Timer::timerTestFunc() { update(); if (getNumUpdates() % 120 == 0) { std::cout << getNumUpdates() << "," << getSeconds() << " Rate: " << ((double)getNumUpdates()/getSeconds()) << "/sec" << std::endl; } if (getNumUpdates() >= 600) { reset(); } } CubicSDR-0.2.3/src/util/Timer.h000066400000000000000000000121371322677621400161250ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #ifndef TIMER_H #define TIMER_H #ifdef WIN32 #include #else #include #endif /// Timer Class, high resolution timer /** * Class provides high resolution timing and useful time control functions */ class Timer { private: unsigned long time_elapsed; unsigned long system_milliseconds; unsigned long start_time; unsigned long end_time; unsigned long last_update; unsigned long num_updates; unsigned long paused_time; unsigned long offset; #ifndef _WIN32 struct timeval time_val; struct timezone time_zone; #else LARGE_INTEGER win_frequency; #endif bool paused_state; bool lock_state; float lock_rate; public: /// Constructor Timer(); /// Start the timer /** * Resets the timer to 0 and begins timing */ void start(void); /// Stop the timer /** * Stops the timer and records the end time */ void stop(void); /// Locks the timer to a specified framerate (for recording / benchmarking purposes typically) /** * Locks the timer to a specified framerate (for recording / benchmarking purposes typically) */ void lockFramerate(float f_rate); /// Unlock any framerate lock that's been applied /** * Unlock any framerate lock that's been applied */ void unlock(); /// Check locked state /** * Check locked state */ bool locked(); /// Reset the timer counter /** * Resetting the timer will reset the current time to 0 */ void reset(void); /// Timer update /** * Calling the update command will bring the timer value up to date, this is meant * to be called at the begining of the frame to establish the time index which is being drawn. */ void update(void); /// Get the total time elapsed since the timer start, not counting paused time /** * Returns the total time elapsed in since the timer start() to the last update() but * does not count the time elapsed while the timer is paused(). * \return Total time elapsed since the timer start() to the last update() excluding time paused() in milliseconds */ unsigned long getMilliseconds(void); /// Alias of getMilliseconds() which returns time in seconds /** * \return Total time elapsed since the timer start() to the last update() excluding time paused() in seconds */ double getSeconds(void); /// Get the total time elapsed since the timer start /** * Returns the total time elapsed in since the timer start() to the last update() * this includes any time accumulated during updates while paused() * \return Total time elapsed since the timer start() to the last update() including time paused() in milliseconds */ unsigned long totalMilliseconds(void); /// Alias of totalMilliseconds() which returns time in seconds /** * \return Total time elapsed since the timer start() to the last update() including time paused() in seconds */ double totalSeconds(void); /// Set the amount of time elapsed /** * Force the timer duration to a specific value, useful for rolling forward or back in a system * based upon the timer. * \param milliseconds_in Time to set timer to in milliseconds */ void setMilliseconds(unsigned long milliseconds_in); /// alias of setMilliseconds() which accepts time in seconds /** * \param seconds_in Time to set timer to in seconds */ void setSeconds(double seconds_in); /// Get the amount of times the update() command has been called /** * By using the number of times the update() command has been called you can easily determine * an average frame rate. Also useful for merely determining how many frames have been drawn. * \return Number of times update() has been called */ unsigned long getNumUpdates(void); /// Get the timer duration during the last update /** * Useful for determining the amount of time which elapsed during the last update * can be used to accurately control values with a per-second rate or determine the current frame rate. * \return Duration of time between the last two calls to update() in milliseconds */ unsigned long lastUpdateMilliseconds(void); /// Alias of lastUpdateMilliseconds() which returns time in seconds /** * \return Duration of time between the last two calls to update() in seconds */ double lastUpdateSeconds(void); /// Set the timer pause state /** * Pause the timer, allowing for continued update() calls without an increment in timing but * maintaining the update and total time count, useful for pausing a scene but allowing frame * timing to resume. * \param pause_in Value to set the current pause state to */ void paused(bool pause_in); /// Check if the timer is currently in a paused state /** * \return Current pause state, true if paused, false otherwise */ bool paused(); void timerTestFunc(); }; #endif CubicSDR-0.2.3/src/visual/000077500000000000000000000000001322677621400152165ustar00rootroot00000000000000CubicSDR-0.2.3/src/visual/ColorTheme.cpp000066400000000000000000000265431322677621400177750ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ColorTheme.h" #include "CubicSDR.h" #include "CubicSDRDefs.h" ThemeMgr ThemeMgr::mgr; void ThemeMgr::setTheme(int themeId) { currentTheme = themes[themeId]; this->themeId = themeId; } int ThemeMgr::getTheme() { return themeId; } ThemeMgr::ThemeMgr() { themes[COLOR_THEME_DEFAULT] = new DefaultColorTheme; themes[COLOR_THEME_BW] = new BlackAndWhiteColorTheme; themes[COLOR_THEME_SHARP] = new SharpColorTheme; themes[COLOR_THEME_RAD] = new RadColorTheme; themes[COLOR_THEME_TOUCH] = new TouchColorTheme; themes[COLOR_THEME_HD] = new HDColorTheme; themes[COLOR_THEME_RADAR] = new RadarColorTheme; currentTheme = themes[COLOR_THEME_DEFAULT]; themeId = COLOR_THEME_DEFAULT; } ThemeMgr::~ThemeMgr() { std::map::iterator i; for (i = themes.begin(); i != themes.end(); i++) { delete i->second; } } DefaultColorTheme::DefaultColorTheme() { name = "Default"; waterfallGradient.addColor(GradientColor(0, 0, 0)); waterfallGradient.addColor(GradientColor(0, 0, 1.0)); waterfallGradient.addColor(GradientColor(0, 1.0, 0)); waterfallGradient.addColor(GradientColor(1.0, 1.0, 0)); waterfallGradient.addColor(GradientColor(1.0, 0.2f, 0.0)); waterfallGradient.generate(256); waterfallHighlight = RGBA4f(1, 1, 1); waterfallNew = RGBA4f(0, 1, 0); waterfallHover = RGBA4f(1, 1, 0); waterfallDestroy = RGBA4f(1, 0, 0); fftLine = RGBA4f(0.9f, 0.9f, 0.9f); fftHighlight = RGBA4f(1, 1, 1); scopeLine = RGBA4f(0.9f, 0.9f, 0.9f); tuningBarLight = RGBA4f(0.2f, 0.2f, 0.9f); tuningBarDark = RGBA4f(0.0f, 0.0f, 0.35f); tuningBarUp = RGBA4f(1.0f, 139.0f/255.0f, 96.0f/255.0f); tuningBarDown = RGBA4f(148.0f/255.0f, 148.0f/255.0f, 1.0f); meterLevel = RGBA4f(0.1f, 0.75f, 0.1f); meterValue = RGBA4f(0.75f, 0.1f, 0.1f); text = RGBA4f(1, 1, 1); freqLine = RGBA4f(1, 1, 1); button = RGBA4f(0.65f, 0.65f, 0.65f); buttonHighlight = RGBA4f(1, 1, 0); scopeBackground = RGBA4f(0.1f, 0.1f, 0.1f); fftBackground = RGBA4f(0.1f, 0.1f, 0.1f); generalBackground = RGBA4f(0.1f, 0.1f, 0.1f); } RadarColorTheme::RadarColorTheme() { name = "Radar"; waterfallGradient.addColor(GradientColor(5.0f / 255.0f, 45.0f / 255.0f, 10.0f / 255.0f)); waterfallGradient.addColor(GradientColor(30.0f / 255.0f, 150.0f / 255.0f, 40.0f / 255.0f)); waterfallGradient.addColor(GradientColor(40.0f / 255.0f, 240.0f / 255.0f, 60.0f / 255.0f)); waterfallGradient.addColor(GradientColor(250.0f / 255.0f, 250.0f / 255.0f, 250.0f / 255.0f)); waterfallGradient.generate(256); waterfallHighlight = RGBA4f(1, 1, 1); waterfallNew = RGBA4f(0, 1, 0); waterfallHover = RGBA4f(1, 1, 0); waterfallDestroy = RGBA4f(1, 0, 0); fftLine = RGBA4f(0.8f, 1.0f, 0.8f); fftHighlight = RGBA4f(1, 1, 1); scopeLine = RGBA4f(0.8f, 1.0f, 0.8f); tuningBarLight = RGBA4f(0.0, 0.45f, 0.0); tuningBarDark = RGBA4f(0.0, 0.1f, 0.0); tuningBarUp = RGBA4f(1.0f, 139.0f/255.0f, 96.0f/255.0f); tuningBarDown = RGBA4f(148.0f/255.0f, 0.0, 0.0); meterLevel = RGBA4f(0, 0.5f, 0); meterValue = RGBA4f(0, 0.5f, 0); text = RGBA4f(0.8f, 1.0, 0.8f); freqLine = RGBA4f(1, 1, 1); button = RGBA4f(0.65f, 0.75f, 0.65f); buttonHighlight = RGBA4f(0.65f, 1.0f, 0.65f); scopeBackground = RGBA4f(0.05f, 0.1f, 0.05f); fftBackground = RGBA4f(0.05f, 0.1f, 0.05f); generalBackground = RGBA4f(0.05f, 0.1f, 0.05f); } BlackAndWhiteColorTheme::BlackAndWhiteColorTheme() { name = "Black & White"; waterfallGradient.addColor(GradientColor(0, 0, 0)); waterfallGradient.addColor(GradientColor(0.75f, 0.75f, 0.75f)); waterfallGradient.addColor(GradientColor(1.0f, 1.0f, 1.0f)); waterfallGradient.generate(256); waterfallHighlight = RGBA4f(1, 1, 0.9f); waterfallNew = RGBA4f(0, 1, 0); waterfallHover = RGBA4f(1, 1, 0); waterfallDestroy = RGBA4f(1, 0, 0); fftLine = RGBA4f(0.9f, 0.9f, 0.9f); fftHighlight = RGBA4f(1, 1, 0.9f); scopeLine = RGBA4f(0.9f, 0.9f, 0.9f); tuningBarLight = RGBA4f(0.4f, 0.4f, 0.4f); tuningBarDark = RGBA4f(0.1f, 0.1f, 0.1f); tuningBarUp = RGBA4f(0.8f, 0.8f, 0.8f); tuningBarDown = RGBA4f(0.4f, 0.4f, 0.4f); meterLevel = RGBA4f(0.5f, 0.5f, 0.5f); meterValue = RGBA4f(0.5f, 0.5f, 0.5f); text = RGBA4f(1, 1, 1); freqLine = RGBA4f(1, 1, 1); button = RGBA4f(0.65f, 0.65f, 0.65f); buttonHighlight = RGBA4f(1, 1, 1); scopeBackground = RGBA4f(0.1f, 0.1f, 0.1f); fftBackground = RGBA4f(0.1f, 0.1f, 0.1f); generalBackground = RGBA4f(0.1f, 0.1f, 0.1f); } SharpColorTheme::SharpColorTheme() { name = "Sharp"; waterfallGradient.addColor(GradientColor(0, 0, 0)); waterfallGradient.addColor(GradientColor(0.0, 0, 0.5f)); waterfallGradient.addColor(GradientColor(0.0, 0.0, 1.0f)); waterfallGradient.addColor(GradientColor(65.0f / 255.0f, 161.0f / 255.0f, 1.0f)); waterfallGradient.addColor(GradientColor(1.0f, 1.0f, 1.0f)); waterfallGradient.addColor(GradientColor(1.0f, 1.0f, 1.0f)); waterfallGradient.addColor(GradientColor(1.0f, 1.0f, 0.5f)); waterfallGradient.addColor(GradientColor(1.0f, 1.0f, 0.0f)); waterfallGradient.addColor(GradientColor(1.0f, 0.5f, 0.0f)); waterfallGradient.addColor(GradientColor(1.0f, 0.25f, 0.0f)); waterfallGradient.addColor(GradientColor(0.5f, 0.1f, 0.0f)); waterfallGradient.generate(256); waterfallHighlight = RGBA4f(0.9f, 0.9f, 1.0f); waterfallNew = RGBA4f(0, 1, 0); waterfallHover = RGBA4f(1, 1, 0); waterfallDestroy = RGBA4f(1, 0, 0); fftLine = RGBA4f(0.9f, 0.9f, 1.0); fftHighlight = RGBA4f(0.9f, 0.9f, 1.0); scopeLine = RGBA4f(0.85f, 0.85f, 1.0); tuningBarLight = RGBA4f(28.0f / 255.0f, 106.0f / 255.0f, 179.0f / 255.0f); tuningBarDark = RGBA4f(14.0f / 255.0f, 53.0f / 255.0f, 89.5f / 255.0f); tuningBarUp = RGBA4f(0.7f, 0.7f, 0.7f); tuningBarDown = RGBA4f(1.0f, 0.0, 0.0); meterLevel = RGBA4f(28.0f / 255.0f, 106.0f / 255.0f, 179.0f / 255.0f); meterValue = RGBA4f(190.0f / 255.0f, 190.0f / 255.0f, 60.0f / 255.0f); text = RGBA4f(0.9f, 0.9f, 1); freqLine = RGBA4f(0.85f, 0.85f, 1.0f); button = RGBA4f(217.0f / 255.0f, 218.0f / 255.0f, 228.0f / 255.0f); buttonHighlight = RGBA4f(208.0f / 255.0f, 249.0f / 255.0f, 255.0f / 255.0f); scopeBackground = RGBA4f(0.05f, 0.05f, 0.15f); fftBackground = RGBA4f(0.05f, 0.05f, 0.15f); generalBackground = RGBA4f(0.05f, 0.05f, 0.15f); } RadColorTheme::RadColorTheme() { name = "Rad"; waterfallGradient.addColor(GradientColor(0, 0, 0.5f)); waterfallGradient.addColor(GradientColor(25.0f / 255.0f, 154.0f / 255.0f, 0.0)); waterfallGradient.addColor(GradientColor(201.0f / 255.0f, 115.0f / 255.0f, 0.0)); waterfallGradient.addColor(GradientColor(1.0, 40.0f / 255.0f, 40.0f / 255.0f)); waterfallGradient.addColor(GradientColor(1.0, 1.0, 1.0)); waterfallGradient.generate(256); waterfallHighlight = RGBA4f(1, 1, 1); waterfallNew = RGBA4f(0, 1, 0); waterfallHover = RGBA4f(1, 1, 0); waterfallDestroy = RGBA4f(1, 0, 0); fftLine = RGBA4f(1.0, 0.9f, 0.9f); fftHighlight = RGBA4f(1, 1, 1); scopeLine = RGBA4f(1.0, 0.9f, 0.9f); tuningBarLight = RGBA4f(0.0, 0.45f, 0.0); tuningBarDark = RGBA4f(0.0, 0.1f, 0.0); tuningBarUp = RGBA4f(1.0, 0.0, 0.0); tuningBarDown = RGBA4f(0.0, 0.5f, 1.0); meterLevel = RGBA4f(0, 0.5f, 0); meterValue = RGBA4f(0.5f, 0, 0); text = RGBA4f(1, 1, 1); freqLine = RGBA4f(1, 1, 1); button = RGBA4f(0.65f, 0.65f, 0.65f); buttonHighlight = RGBA4f(0.76f, 0.65f, 0); scopeBackground = RGBA4f(13.0f / 255.0f, 47.0f / 255.0f, 9.0f / 255.0f); fftBackground = RGBA4f(0, 0, 50.0f / 255.0f); generalBackground = RGBA4f(13.0f / 255.0f, 47.0f / 255.0f, 9.0f / 255.0f); } TouchColorTheme::TouchColorTheme() { name = "Touch"; waterfallGradient.addColor(GradientColor(0, 0, 0)); waterfallGradient.addColor(GradientColor(55.0f / 255.0f, 40.0f / 255.0f, 55.0f / 255.0f)); waterfallGradient.addColor(GradientColor(61.0f / 255.0f, 57.0f / 255.0f, 88.0f / 255.0f)); waterfallGradient.addColor(GradientColor(0.0f / 255.0f, 255.0f / 255.0f, 255.0f / 255.0f)); waterfallGradient.addColor(GradientColor(10.0f / 255.0f, 255.0f / 255.0f, 85.0f / 255.0f)); waterfallGradient.addColor(GradientColor(255.0f / 255.0f, 255.0f / 255.0f, 75.0f / 255.0f)); waterfallGradient.addColor(GradientColor(255.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f)); waterfallGradient.addColor(GradientColor(255.0f / 255.0f, 255.0f / 255.0f, 255.0f / 255.0f)); waterfallGradient.generate(256); waterfallHighlight = RGBA4f(1, 1, 1); waterfallNew = RGBA4f(0, 1, 0); waterfallHover = RGBA4f(1, 1, 0); waterfallDestroy = RGBA4f(1, 0, 0); fftLine = RGBA4f(234.0f / 255.0f, 232.0f / 255.0f, 247.0f / 255.0f); fftHighlight = RGBA4f(1.0f, 1.0f, 1.0f); scopeLine = RGBA4f(234.0f / 255.0f, 232.0f / 255.0f, 247.0f / 255.0f); tuningBarLight = RGBA4f(0.2f, 0.2f, 0.7f); tuningBarDark = RGBA4f(0.1f, 0.1f, 0.45f); tuningBarUp = RGBA4f(0.5f, 139.0f/255.0f, 96.0f/255.0f); tuningBarDown = RGBA4f(0.6f, 108.0f/255.0f, 1.0f); meterLevel = RGBA4f(61.0f / 255.0f, 57.0f / 255.0f, 88.0f / 255.0f); meterValue = RGBA4f(61.0f / 255.0f, 57.0f / 255.0f, 88.0f / 255.0f); text = RGBA4f(1, 1, 1); freqLine = RGBA4f(1, 1, 1); button = RGBA4f(1.0f, 1.0f, 1.0f); buttonHighlight = RGBA4f(208.0f / 255.0f, 202.0f / 255.0f, 247.0f / 255.0f); scopeBackground = RGBA4f(39.0f / 255.0f, 36.0f / 255.0f, 56.0f / 255.0f); fftBackground = RGBA4f(39.0f / 255.0f, 36.0f / 255.0f, 56.0f / 255.0f); generalBackground = RGBA4f(61.0f / 255.0f, 57.0f / 255.0f, 88.0f / 255.0f); } HDColorTheme::HDColorTheme() { name = "HD"; waterfallGradient.addColor(GradientColor(5.0f / 255.0f, 5.0f / 255.0f, 60.0f / 255.0f)); waterfallGradient.addColor(GradientColor(5.0f / 255.0f, 20.0f / 255.0f, 120.0f / 255.0f)); waterfallGradient.addColor(GradientColor(50.0f / 255.0f, 100.0f / 255.0f, 200.0f / 255.0f)); waterfallGradient.addColor(GradientColor(75.0f / 255.0f, 190.0f / 255.0f, 100.0f / 255.0f)); waterfallGradient.addColor(GradientColor(240.0f / 255.0f, 55.0f / 255.0f, 5.0f / 255.0f)); waterfallGradient.addColor(GradientColor(255.0f / 255.0f, 55.0f / 255.0f, 100.0f / 255.0f)); waterfallGradient.addColor(GradientColor(255.0f / 255.0f, 235.0f / 255.0f, 100.0f / 255.0f)); waterfallGradient.addColor(GradientColor(250.0f / 255.0f, 250.0f / 255.0f, 250.0f / 255.0f)); waterfallGradient.generate(256); waterfallHighlight = RGBA4f(1, 1, 1); waterfallNew = RGBA4f(0, 1, 0); waterfallHover = RGBA4f(1, 1, 0); waterfallDestroy = RGBA4f(1, 0, 0); fftLine = RGBA4f(0.9f, 0.9f, 0.9f); fftHighlight = RGBA4f(1, 1, 1); scopeLine = RGBA4f(0.9f, 0.9f, 0.9f); tuningBarLight = RGBA4f(0.4f, 0.4f, 1.0); tuningBarDark = RGBA4f(0.1f, 0.1f, 0.45f); tuningBarUp = RGBA4f(1.0, 139.0f/255.0f, 96.0f/255.0f); tuningBarDown = RGBA4f(148.0f/255.0f, 148.0f/255.0f, 1.0f); meterLevel = RGBA4f(0, 0.5f, 0); meterValue = RGBA4f(0.0, 0.0, 1.0); text = RGBA4f(1, 1, 1); freqLine = RGBA4f(1, 1, 1); button = RGBA4f(0, 0.7f, 0.7f); buttonHighlight = RGBA4f(1, 1, 1); scopeBackground = RGBA4f(0.0, 0.0, 48.0f / 255.0f); fftBackground = RGBA4f(0.0, 0.0, 48.0f / 255.0f); generalBackground = RGBA4f(0.0, 0.0, 0.0); } CubicSDR-0.2.3/src/visual/ColorTheme.h000066400000000000000000000047251322677621400174400ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "Gradient.h" #include #include #include #include #define COLOR_THEME_DEFAULT 0 #define COLOR_THEME_BW 1 #define COLOR_THEME_SHARP 2 #define COLOR_THEME_RAD 3 #define COLOR_THEME_TOUCH 4 #define COLOR_THEME_HD 5 #define COLOR_THEME_RADAR 6 #define COLOR_THEME_MAX 7 class RGBA4f { public: float r, g, b, a; RGBA4f(float r, float g, float b, float a = 1.0) : r(r), g(g), b(b), a(a) { } RGBA4f() : RGBA4f(0, 0, 0) { } ~RGBA4f() { } RGBA4f & operator=(const RGBA4f &other) { r = other.r; g = other.g; b = other.b; a = other.a; return *this; } RGBA4f operator*(float v) { return RGBA4f(r*v, g*v, b*v); } operator wxColour() { return wxColour( (unsigned char) std::min((r * 255.0), 255.0), (unsigned char) std::min((g * 255.0), 255.0), (unsigned char) std::min((b * 255.0), 255.0)); } }; class ColorTheme { public: RGBA4f waterfallHighlight; RGBA4f waterfallNew; RGBA4f wfHighlight; RGBA4f waterfallHover; RGBA4f waterfallDestroy; RGBA4f fftLine; RGBA4f fftHighlight; RGBA4f scopeLine; RGBA4f tuningBarLight; RGBA4f tuningBarDark; RGBA4f tuningBarUp; RGBA4f tuningBarDown; RGBA4f meterLevel; RGBA4f meterValue; RGBA4f text; RGBA4f freqLine; RGBA4f button; RGBA4f buttonHighlight; RGBA4f scopeBackground; RGBA4f fftBackground; RGBA4f generalBackground; Gradient waterfallGradient; std::string name; }; class ThemeMgr { public: ThemeMgr(); ~ThemeMgr(); ColorTheme *currentTheme; std::map themes; void setTheme(int themeId); int getTheme(); int themeId; static ThemeMgr mgr; }; class DefaultColorTheme: public ColorTheme { public: DefaultColorTheme(); }; class BlackAndWhiteColorTheme: public ColorTheme { public: BlackAndWhiteColorTheme(); }; class SharpColorTheme: public ColorTheme { public: SharpColorTheme(); }; class RadColorTheme: public ColorTheme { public: RadColorTheme(); }; class TouchColorTheme: public ColorTheme { public: TouchColorTheme(); }; class HDColorTheme: public ColorTheme { public: HDColorTheme(); }; class RadarColorTheme: public ColorTheme { public: RadarColorTheme(); }; CubicSDR-0.2.3/src/visual/GainCanvas.cpp000066400000000000000000000206351322677621400177420ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "GainCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include #include wxBEGIN_EVENT_TABLE(GainCanvas, wxGLCanvas) EVT_PAINT(GainCanvas::OnPaint) EVT_IDLE(GainCanvas::OnIdle) EVT_MOTION(GainCanvas::OnMouseMoved) EVT_LEFT_DOWN(GainCanvas::OnMouseDown) EVT_LEFT_UP(GainCanvas::OnMouseReleased) EVT_LEAVE_WINDOW(GainCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(GainCanvas::OnMouseEnterWindow) EVT_MOUSEWHEEL(GainCanvas::OnMouseWheelMoved) wxEND_EVENT_TABLE() GainCanvas::GainCanvas(wxWindow *parent, std::vector dispAttrs) : InteractiveCanvas(parent, dispAttrs) { glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); bgPanel.setCoordinateSystem(GLPanel::GLPANEL_Y_UP); bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_X); numGains = 1; spacing = 2.0/numGains; barWidth = (1.0/numGains)*0.8; startPos = spacing/2.0; barHeight = 0.8f; refreshCounter = 0; userGainAsChanged = false; } GainCanvas::~GainCanvas() { } void GainCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); bgPanel.draw(); SwapBuffers(); } void GainCanvas::OnIdle(wxIdleEvent &event) { if (mouseTracker.mouseInView()) { Refresh(); } else { event.Skip(); } bool areGainsChangedHere = false; for (auto gi : gainPanels) { if (gi->getChanged()) { areGainsChangedHere = true; // Gain only displays integer gain values, so set the applied gain //value to exactly that. wxGetApp().setGain(gi->getName(), (int)(gi->getValue())); //A gain may be exposed as setting also so assure refresh of the menu also. wxGetApp().notifyMainUIOfDeviceChange(false); //do not rebuild the gain UI gi->setChanged(false); } } //User input has changed the gain, so schedule an update of values //in 150ms in the future, else the device may not have taken the value into account. if (areGainsChangedHere) { userGainAsChanged = true; userGainAsChangedDelayTimer.start(); } else { userGainAsChangedDelayTimer.update(); if (!userGainAsChanged || (userGainAsChanged && userGainAsChangedDelayTimer.getMilliseconds() > 150)) { if (updateGainValues()) { Refresh(); } userGainAsChanged = false; } } } void GainCanvas::SetLevel() { CubicVR::vec2 mpos = mouseTracker.getGLXY(); for (auto gi : gainPanels) { if (gi->isMeterHit(mpos)) { float value = gi->getMeterHitValue(mpos); gi->setValue(value); gi->setChanged(true); break; } } } void GainCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); CubicVR::vec2 mpos = mouseTracker.getGLXY(); for (auto gi : gainPanels) { if (gi->isMeterHit(mpos)) { float value = gi->getMeterHitValue(mpos); gi->setHighlight(value); gi->setHighlightVisible(true); wxGetApp().setActiveGainEntry(gi->getName()); } else { gi->setHighlightVisible(false); } } if (mouseTracker.mouseDown()) { SetLevel(); } else { if (!helpTip.empty()) { setStatusText(helpTip); } } } void GainCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); SetLevel(); } void GainCanvas::OnMouseWheelMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseWheelMoved(event); CubicVR::vec2 hitResult; CubicVR::vec2 mpos = mouseTracker.getGLXY(); for (auto gi : gainPanels) { if (gi->isMeterHit(mpos)) { float movement = 3.0 * (float)event.GetWheelRotation(); gi->setValue(gi->getValue() + ((movement / 100.0) * ((gi->getHigh() - gi->getLow()) / 100.0))); gi->setChanged(true); break; } } } void GainCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); } void GainCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor(wxCURSOR_CROSS); for (auto gi : gainPanels) { gi->setHighlightVisible(false); } Refresh(); } void GainCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); SetCursor(wxCURSOR_CROSS); #ifdef _WIN32 if (wxGetApp().getAppFrame()->canFocus()) { this->SetFocus(); } #endif } void GainCanvas::setHelpTip(std::string tip) { helpTip = tip; } void GainCanvas::updateGainUI() { SDRDeviceInfo *devInfo = wxGetApp().getDevice(); //possible if we 'Refresh Devices' then devInfo becomes null //until a new device is selected. if (devInfo == nullptr) { return; } DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId()); //read the gains from the device. //This may be wrong because the device is not started, or has yet //to take into account a user gain change. Doesn't matter, //UpdateGainValues() takes cares of updating the true value realtime. gains = devInfo->getGains(SOAPY_SDR_RX, 0); SDRRangeMap::iterator gi; numGains = gains.size(); float i = 0; if (!numGains) { return; } spacing = 2.0/numGains; barWidth = (1.0/numGains)*0.7; startPos = spacing/2.0; barHeight = 1.0f; while (gainPanels.size()) { MeterPanel *mDel = gainPanels.back(); gainPanels.pop_back(); bgPanel.removeChild(mDel); delete mDel; } for (auto gi : gains) { MeterPanel *mPanel = new MeterPanel(gi.first, gi.second.minimum(), gi.second.maximum(), devConfig->getGain(gi.first,wxGetApp().getGain(gi.first))); float midPos = -1.0+startPos+spacing*i; mPanel->setPosition(midPos, 0); mPanel->setSize(barWidth, barHeight); bgPanel.addChild(mPanel); gainPanels.push_back(mPanel); i++; } setThemeColors(); } // call this to refresh the gain values only, not the whole UI. bool GainCanvas::updateGainValues() { bool isRefreshNeeded = false; SDRDeviceInfo *devInfo = wxGetApp().getDevice(); //possible if we 'Refresh Devices' then devInfo becomes null //until a new device is selected. //also, do not attempt an update with the device is not started. if (devInfo == nullptr || !devInfo->isActive()) { return false; } DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId()); gains = devInfo->getGains(SOAPY_SDR_RX, 0); SDRRangeMap::iterator gi; size_t numGainsToRefresh = std::min(gains.size(), gainPanels.size()); size_t panelIndex = 0; //actually the order of gains iteration should be constant because map of string, //and gainPanels were built in that order in updateGainUI() for (auto gi : gains) { if (panelIndex >= numGainsToRefresh) { break; } // do not update if a change is already pending. if (!gainPanels[panelIndex]->getChanged()) { //read the actual gain from the device, round it float actualRoundedGain = (float)std::round(devInfo->getCurrentGain(SOAPY_SDR_RX, 0, gi.first)); //do nothing if the difference is less than 1.0, since the panel do not show it anyway. if ((int)actualRoundedGain != (int)(gainPanels[panelIndex]->getValue())) { gainPanels[panelIndex]->setValue(actualRoundedGain); //update the config with this value : //a consequence of such updates is that the use setting // is overriden by the current one in AGC mode. //TODO: if it not desirable, do not update in AGC mode. devConfig->setGain(gi.first, actualRoundedGain); isRefreshNeeded = true; } } //end if no external change pending. panelIndex++; } return isRefreshNeeded; } void GainCanvas::setThemeColors() { RGBA4f c1, c2; c1 = ThemeMgr::mgr.currentTheme->generalBackground; c2 = ThemeMgr::mgr.currentTheme->generalBackground * 0.5; c1.a = 1.0; c2.a = 1.0; bgPanel.setFillColor(c1, c2); Refresh(); } CubicSDR-0.2.3/src/visual/GainCanvas.h000066400000000000000000000027321322677621400174050ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include #include #include #include "InteractiveCanvas.h" #include "MouseTracker.h" #include "GLPanel.h" #include "PrimaryGLContext.h" #include "SDRDeviceInfo.h" #include "Timer.h" #include "MeterPanel.h" class GainCanvas: public InteractiveCanvas { public: GainCanvas(wxWindow *parent, std::vector dispAttrs); ~GainCanvas(); void setHelpTip(std::string tip); void updateGainUI(); void setThemeColors(); private: // call this to refresh the gain values only, return true if refresh is needed bool updateGainValues(); void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); void SetLevel(); void OnShow(wxShowEvent& event); void OnMouseMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); PrimaryGLContext *glContext; std::string helpTip; std::vector gainPanels; GLPanel bgPanel; SDRRangeMap gains; float spacing, barWidth, startPos, barHeight, numGains; int refreshCounter; wxSize clientSize; std::atomic_bool userGainAsChanged; Timer userGainAsChangedDelayTimer; // wxDECLARE_EVENT_TABLE(); }; CubicSDR-0.2.3/src/visual/ImagePanel.cpp000066400000000000000000000017371322677621400177340ustar00rootroot00000000000000#include "ImagePanel.h" BEGIN_EVENT_TABLE(ImagePanel, wxPanel) EVT_PAINT(ImagePanel::paintEvent) END_EVENT_TABLE() ImagePanel::ImagePanel(wxPanel * parent, wxString file, wxBitmapType format) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE) { image.LoadFile(file, format); } void ImagePanel::paintEvent(wxPaintEvent & evt) { wxPaintDC dc(this); render(dc); } void ImagePanel::paintNow() { wxClientDC dc(this); render(dc); } void ImagePanel::render(wxDC& dc) { double imagew = image.GetWidth(); double imageh = image.GetHeight(); wxSize destSize = dc.GetSize(); double destw = destSize.GetWidth(); double desth = destSize.GetHeight(); double sf = 1.0, wf, hf; wf = destw / imagew; hf = desth / imageh; sf = (wf < hf)?wf:hf; double resulth = imageh * sf; double resultw = imagew * sf; dc.SetUserScale(sf, sf); dc.DrawBitmap( image, (destw/2 - resultw/2)/sf, (desth/2 - resulth/2)/sf, false ); } CubicSDR-0.2.3/src/visual/ImagePanel.h000066400000000000000000000004301322677621400173660ustar00rootroot00000000000000#include #include class ImagePanel : public wxPanel { wxBitmap image; public: ImagePanel(wxPanel* parent, wxString file, wxBitmapType format); void paintEvent(wxPaintEvent & evt); void paintNow(); void render(wxDC& dc); DECLARE_EVENT_TABLE() }; CubicSDR-0.2.3/src/visual/InteractiveCanvas.cpp000066400000000000000000000112021322677621400213270ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "InteractiveCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include #include InteractiveCanvas::InteractiveCanvas(wxWindow *parent, std::vector dispAttrs) : wxGLCanvas(parent, wxID_ANY, dispAttrs.data(), wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), centerFreq(0), bandwidth(0), lastBandwidth(0), isView( false) { mouseTracker.setTarget(this); } InteractiveCanvas::~InteractiveCanvas() { } void InteractiveCanvas::setView(long long center_freq_in, long long bandwidth_in) { isView = true; centerFreq = center_freq_in; bandwidth = bandwidth_in; lastBandwidth = 0; } void InteractiveCanvas::disableView() { isView = false; centerFreq = wxGetApp().getFrequency(); bandwidth = wxGetApp().getSampleRate(); lastBandwidth = 0; } bool InteractiveCanvas::getViewState() { return isView; } long long InteractiveCanvas::getFrequencyAt(float x) { long long iqCenterFreq = getCenterFrequency(); long long iqBandwidth = getBandwidth(); long long freq = iqCenterFreq - (long long)(0.5 * (long double) iqBandwidth) + ((long double) x * (long double) iqBandwidth); return freq; } long long InteractiveCanvas::getFrequencyAt(float x, long long iqCenterFreq, long long iqBandwidth) { long long freq = iqCenterFreq - (long long)(0.5 * (long double) iqBandwidth) + ((long double) x * (long double) iqBandwidth); return freq; } void InteractiveCanvas::setCenterFrequency(long long center_freq_in) { centerFreq = center_freq_in; } long long InteractiveCanvas::getCenterFrequency() { if (isView) { return centerFreq; } else { return wxGetApp().getFrequency(); } } void InteractiveCanvas::setBandwidth(long long bandwidth_in) { bandwidth = bandwidth_in; } long long InteractiveCanvas::getBandwidth() { if (isView) { return bandwidth; } else { return wxGetApp().getSampleRate(); } } MouseTracker *InteractiveCanvas::getMouseTracker() { return &mouseTracker; } bool InteractiveCanvas::isAltDown() { return altDown; } bool InteractiveCanvas::isCtrlDown() { return ctrlDown; } bool InteractiveCanvas::isShiftDown() { return shiftDown; } void InteractiveCanvas::OnKeyUp(wxKeyEvent& event) { shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); } void InteractiveCanvas::OnKeyDown(wxKeyEvent& event) { shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); } void InteractiveCanvas::OnMouseMoved(wxMouseEvent& event) { mouseTracker.OnMouseMoved(event); shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); } void InteractiveCanvas::OnMouseDown(wxMouseEvent& event) { mouseTracker.OnMouseDown(event); shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); } void InteractiveCanvas::OnMouseWheelMoved(wxMouseEvent& event) { mouseTracker.OnMouseWheelMoved(event); } void InteractiveCanvas::OnMouseReleased(wxMouseEvent& event) { mouseTracker.OnMouseReleased(event); shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); } void InteractiveCanvas::OnMouseLeftWindow(wxMouseEvent& event) { mouseTracker.OnMouseLeftWindow(event); shiftDown = false; altDown = false; ctrlDown = false; } void InteractiveCanvas::OnMouseEnterWindow(wxMouseEvent& event) { mouseTracker.OnMouseEnterWindow(event); shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); } void InteractiveCanvas::setStatusText(std::string statusText) { wxGetApp().getAppFrame()->setStatusText(this, statusText); } void InteractiveCanvas::setStatusText(std::string statusText, int value) { wxGetApp().getAppFrame()->setStatusText(statusText, value); } void InteractiveCanvas::OnMouseRightDown(wxMouseEvent& event) { mouseTracker.OnMouseRightDown(event); } void InteractiveCanvas::OnMouseRightReleased(wxMouseEvent& event) { mouseTracker.OnMouseRightReleased(event); } bool InteractiveCanvas::isMouseInView() { return mouseTracker.mouseInView(); } bool InteractiveCanvas::isMouseDown() { return mouseTracker.mouseInView() && mouseTracker.mouseDown(); } CubicSDR-0.2.3/src/visual/InteractiveCanvas.h000066400000000000000000000033201322677621400207760ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include "MouseTracker.h" #include #include class InteractiveCanvas: public wxGLCanvas { public: InteractiveCanvas(wxWindow *parent, std::vector dispAttrs); virtual ~InteractiveCanvas(); long long getFrequencyAt(float x); long long getFrequencyAt(float x, long long iqCenterFreq, long long iqBandwidth); virtual void setView(long long center_freq_in, long long bandwidth_in); virtual void disableView(); bool getViewState(); void setCenterFrequency(long long center_freq_in); long long getCenterFrequency(); void setBandwidth(long long bandwidth_in); long long getBandwidth(); MouseTracker *getMouseTracker(); bool isMouseInView(); bool isMouseDown(); bool isAltDown(); bool isCtrlDown(); bool isShiftDown(); protected: void OnKeyDown(wxKeyEvent& event); void OnKeyUp(wxKeyEvent& event); void OnMouseMoved(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseRightDown(wxMouseEvent& event); void OnMouseRightReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); void setStatusText(std::string statusText); void setStatusText(std::string statusText, int value); wxWindow *parent; MouseTracker mouseTracker; bool shiftDown; bool altDown; bool ctrlDown; long long centerFreq; long long bandwidth; long long lastBandwidth; bool isView; }; CubicSDR-0.2.3/src/visual/MeterCanvas.cpp000066400000000000000000000121261322677621400201340ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "MeterCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include wxBEGIN_EVENT_TABLE(MeterCanvas, wxGLCanvas) EVT_PAINT(MeterCanvas::OnPaint) EVT_IDLE(MeterCanvas::OnIdle) EVT_MOTION(MeterCanvas::OnMouseMoved) EVT_LEFT_DOWN(MeterCanvas::OnMouseDown) EVT_LEFT_UP(MeterCanvas::OnMouseReleased) EVT_MOUSEWHEEL(MeterCanvas::OnMouseWheelMoved) EVT_RIGHT_DOWN(MeterCanvas::OnMouseRightDown) EVT_RIGHT_UP(MeterCanvas::OnMouseRightReleased) EVT_LEAVE_WINDOW(MeterCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(MeterCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() MeterCanvas::MeterCanvas(wxWindow *parent, std::vector dispAttrs) : InteractiveCanvas(parent, dispAttrs), level(0), level_min(0), level_max(1), inputValue(0), userInputValue(0), showUserInput(true) { glContext = new MeterContext(this, &wxGetApp().GetContext(this)); } MeterCanvas::~MeterCanvas() { } void MeterCanvas::setLevel(float level_in) { level = level_in; Refresh(); } float MeterCanvas::getLevel() { return level; } void MeterCanvas::setMax(float max_in) { level_max = max_in; Refresh(); } void MeterCanvas::setMin(float min_in) { level_min = min_in; Refresh(); } void MeterCanvas::setUserInputValue(float slider_in) { userInputValue = slider_in; Refresh(); } void MeterCanvas::setInputValue(float slider_in) { userInputValue = inputValue = slider_in; Refresh(); } bool MeterCanvas::inputChanged() { return (inputValue != userInputValue); } float MeterCanvas::getInputValue() { inputValue = userInputValue; return userInputValue; } void MeterCanvas::setShowUserInput(bool showUserInput) { this->showUserInput = showUserInput; } void MeterCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); glContext->DrawBegin(); glContext->Draw(ThemeMgr::mgr.currentTheme->generalBackground.r, ThemeMgr::mgr.currentTheme->generalBackground.g, ThemeMgr::mgr.currentTheme->generalBackground.b, 0.5, 1.0); if (mouseTracker.mouseInView()) { glContext->Draw(0.4f, 0.4f, 0.4f, 0.5f, mouseTracker.getMouseY()); } glContext->Draw(ThemeMgr::mgr.currentTheme->meterLevel.r, ThemeMgr::mgr.currentTheme->meterLevel.g, ThemeMgr::mgr.currentTheme->meterLevel.b, 0.5, (level-level_min) / (level_max-level_min)); if (showUserInput) { glContext->Draw(ThemeMgr::mgr.currentTheme->meterValue.r, ThemeMgr::mgr.currentTheme->meterValue.g, ThemeMgr::mgr.currentTheme->meterValue.b, 0.5, (userInputValue-level_min) / (level_max-level_min)); } glContext->DrawEnd(); SwapBuffers(); } void MeterCanvas::OnIdle(wxIdleEvent &event) { if (mouseTracker.mouseInView()) { Refresh(); } else { event.Skip(); } } void MeterCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); if (mouseTracker.mouseDown()) { userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min; } else { if (!helpTip.empty()) { setStatusText(helpTip); } } } void MeterCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min; mouseTracker.setHorizDragLock(true); } void MeterCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min; } void MeterCanvas::OnMouseRightDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseRightDown(event); } void MeterCanvas::OnMouseRightReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseRightReleased(event); if (showUserInput) { userInputValue = level - level * 0.02; } } void MeterCanvas::OnMouseWheelMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseWheelMoved(event); float movement = 3.0 * (float)event.GetWheelRotation(); float currentValue = 0; if (showUserInput) { currentValue = userInputValue; } else { currentValue = level; } currentValue = currentValue + ((movement / 100.0) * ((level_max - level_min) / 100.0)); if (currentValue > level_max) { currentValue = level_max; } if (currentValue < level_min) { currentValue = level_min; } userInputValue = currentValue; } void MeterCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor(wxCURSOR_CROSS); Refresh(); } void MeterCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); SetCursor(wxCURSOR_CROSS); #ifdef _WIN32 if (wxGetApp().getAppFrame()->canFocus()) { this->SetFocus(); } #endif } void MeterCanvas::setHelpTip(std::string tip) { helpTip = tip; } CubicSDR-0.2.3/src/visual/MeterCanvas.h000066400000000000000000000026361322677621400176060ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include #include #include "InteractiveCanvas.h" #include "MeterContext.h" #include "MouseTracker.h" #include "Timer.h" class MeterCanvas: public InteractiveCanvas { public: MeterCanvas(wxWindow *parent, std::vector dispAttrs); ~MeterCanvas(); void setLevel(float level_in); float getLevel(); void setMax(float max_in); void setMin(float max_in); void setUserInputValue(float slider_in); void setInputValue(float slider_in); bool inputChanged(); float getInputValue(); void setShowUserInput(bool showUserInput); void setHelpTip(std::string tip); private: void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); void OnMouseMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseRightDown(wxMouseEvent& event); void OnMouseRightReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); MeterContext *glContext; float level; float level_min, level_max; float inputValue; float userInputValue; bool showUserInput; std::string helpTip; // wxDECLARE_EVENT_TABLE(); }; CubicSDR-0.2.3/src/visual/MeterContext.cpp000066400000000000000000000025271322677621400203510ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "MeterContext.h" #include "MeterCanvas.h" #include "ColorTheme.h" MeterContext::MeterContext(MeterCanvas *canvas, wxGLContext *sharedContext) : PrimaryGLContext(canvas, sharedContext) { } void MeterContext::DrawBegin() { glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glClearColor(ThemeMgr::mgr.currentTheme->generalBackground.r, ThemeMgr::mgr.currentTheme->generalBackground.g, ThemeMgr::mgr.currentTheme->generalBackground.b, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_TEXTURE_2D); } void MeterContext::Draw(float r, float g, float b, float a, float level) { glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); glBegin(GL_QUADS); glColor4f(r*0.65,g*0.65,b*0.65,a); glVertex2f(-1.0, -1.0 + 2.0 * level); glVertex2f(-1.0, -1.0); glColor4f(r,g,b,a); glVertex2f(0.0, -1.0); glVertex2f(0.0, -1.0 + 2.0 * level); glColor4f(r,g,b,a); glVertex2f(0.0, -1.0 + 2.0 * level); glVertex2f(0.0, -1.0); glColor4f(r*0.65,g*0.65,b*0.65,a*0.65); glVertex2f(1.0, -1.0); glVertex2f(1.0, -1.0 + 2.0 * level); glEnd(); glDisable(GL_BLEND); } void MeterContext::DrawEnd() { // glFlush(); // CheckGLError(); } CubicSDR-0.2.3/src/visual/MeterContext.h000066400000000000000000000006621322677621400200140ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "PrimaryGLContext.h" #include "Gradient.h" #define NUM_WATERFALL_LINES 512 class MeterCanvas; class MeterContext: public PrimaryGLContext { public: MeterContext(MeterCanvas *canvas, wxGLContext *sharedContext); void DrawBegin(); void Draw(float r, float g, float b, float a, float level); void DrawEnd(); private: }; CubicSDR-0.2.3/src/visual/ModeSelectorCanvas.cpp000066400000000000000000000134501322677621400214460ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModeSelectorCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include wxBEGIN_EVENT_TABLE(ModeSelectorCanvas, wxGLCanvas) EVT_PAINT(ModeSelectorCanvas::OnPaint) EVT_IDLE(ModeSelectorCanvas::OnIdle) EVT_MOTION(ModeSelectorCanvas::OnMouseMoved) EVT_LEFT_DOWN(ModeSelectorCanvas::OnMouseDown) EVT_LEFT_UP(ModeSelectorCanvas::OnMouseReleased) EVT_LEAVE_WINDOW(ModeSelectorCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(ModeSelectorCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() ModeSelectorCanvas::ModeSelectorCanvas(wxWindow *parent, std::vector dispAttrs) : InteractiveCanvas(parent, dispAttrs), numChoices(0), currentSelection(-1), toggleMode(false), inputChanged(false), padX(4.0), padY(4.0), highlightOverride(false) { glContext = new ModeSelectorContext(this, &wxGetApp().GetContext(this)); highlightColor = RGBA4f(1.0,1.0,1.0,1.0); } ModeSelectorCanvas::~ModeSelectorCanvas() { } int ModeSelectorCanvas::getHoveredSelection() { if (!mouseTracker.mouseInView()) { return -1; } float ypos = 1.0 - (mouseTracker.getMouseY() * 2.0); float yval = (int) (((ypos + 1.0) / 2.0) * (float) numChoices); return yval; } void ModeSelectorCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); glContext->DrawBegin(); int yval = getHoveredSelection(); for (int i = 0; i < numChoices; i++) { if (yval == i && !highlightOverride) { RGBA4f hc = ThemeMgr::mgr.currentTheme->buttonHighlight; glContext->DrawSelector(selections[i].label, i, numChoices, true, hc.r, hc.g, hc.b, 1.0, padX, padY); } else { RGBA4f hc = ThemeMgr::mgr.currentTheme->button; if (highlightOverride) { hc = highlightColor; } glContext->DrawSelector(selections[i].label, i, numChoices, i == currentSelection, hc.r, hc.g, hc.b, 1.0, padX, padY); } } glContext->DrawEnd(); SwapBuffers(); } void ModeSelectorCanvas::OnIdle(wxIdleEvent &event) { if (mouseTracker.mouseInView()) { Refresh(); } else { event.Skip(); } } void ModeSelectorCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); } void ModeSelectorCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); mouseTracker.setHorizDragLock(true); mouseTracker.setVertDragLock(true); } void ModeSelectorCanvas::OnMouseWheelMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseWheelMoved(event); } void ModeSelectorCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); mouseTracker.setHorizDragLock(false); mouseTracker.setVertDragLock(false); const wxSize ClientSize = GetClientSize(); int selectedButton = currentSelection; if (mouseTracker.getOriginDeltaMouseX() < 2.0 / ClientSize.y) { selectedButton = getHoveredSelection(); } if (toggleMode && (currentSelection == selectedButton)) { selectedButton = -1; } if (currentSelection != selectedButton) { inputChanged = true; } currentSelection = selectedButton; SetCursor (wxCURSOR_HAND); Refresh(); } void ModeSelectorCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor (wxCURSOR_CROSS); Refresh(); } void ModeSelectorCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); SetCursor (wxCURSOR_HAND); if (!helpTip.empty()) { setStatusText(helpTip); } Refresh(); } void ModeSelectorCanvas::setHelpTip(std::string tip) { helpTip = tip; } void ModeSelectorCanvas::setNumChoices(int numChoices_in) { numChoices = numChoices_in; Refresh(); } void ModeSelectorCanvas::addChoice(int value, std::string label) { selections.push_back(ModeSelectorMode(value, label)); numChoices = selections.size(); } void ModeSelectorCanvas::addChoice(std::string label) { selections.push_back(ModeSelectorMode(selections.size()+1, label)); numChoices = selections.size(); } void ModeSelectorCanvas::setSelection(std::string label) { for (int i = 0; i < numChoices; i++) { if (selections[i].label == label) { currentSelection = i; Refresh(); return; } } currentSelection = -1; Refresh(); } std::string ModeSelectorCanvas::getSelectionLabel() { if (currentSelection == -1) { return ""; } return selections[currentSelection].label; } void ModeSelectorCanvas::setSelection(int value) { for (int i = 0; i < numChoices; i++) { if (selections[i].value == value) { currentSelection = i; Refresh(); return; } } currentSelection = -1; Refresh(); } int ModeSelectorCanvas::getSelection() { if (currentSelection == -1) { return -1; } return selections[currentSelection].value; } void ModeSelectorCanvas::setToggleMode(bool toggleMode) { this->toggleMode = toggleMode; } bool ModeSelectorCanvas::modeChanged() { return inputChanged; } void ModeSelectorCanvas::clearModeChanged() { inputChanged = false; } void ModeSelectorCanvas::setPadding(float padX, float padY) { this->padX = padX; this->padY = padY; } void ModeSelectorCanvas::setHighlightColor(RGBA4f hc) { this->highlightColor = hc; this->highlightOverride = true; } CubicSDR-0.2.3/src/visual/ModeSelectorCanvas.h000066400000000000000000000034301322677621400211100ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include #include #include "InteractiveCanvas.h" #include "ModeSelectorContext.h" #include "MouseTracker.h" #include "Timer.h" class ModeSelectorMode { public: int value; std::string label; ModeSelectorMode(int value, std::string label) : value(value), label(label) { } }; class ModeSelectorCanvas: public InteractiveCanvas { public: ModeSelectorCanvas(wxWindow *parent, std::vector dispAttrs); ~ModeSelectorCanvas(); int getHoveredSelection(); void setHelpTip(std::string tip); void addChoice(int value, std::string label); void addChoice(std::string label); void setSelection(std::string label); std::string getSelectionLabel(); void setSelection(int value); int getSelection(); void setToggleMode(bool toggleMode); bool modeChanged(); void clearModeChanged(); void setPadding(float padX, float padY); void setHighlightColor(RGBA4f hc); private: void setNumChoices(int numChoices_in); void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); void OnMouseMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); ModeSelectorContext *glContext; std::string helpTip; int numChoices; int currentSelection; bool toggleMode; bool inputChanged; std::vector selections; float padX, padY; RGBA4f highlightColor; bool highlightOverride; // wxDECLARE_EVENT_TABLE(); }; CubicSDR-0.2.3/src/visual/ModeSelectorContext.cpp000066400000000000000000000037031322677621400216570ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ModeSelectorContext.h" #include "ModeSelectorCanvas.h" #include "ColorTheme.h" ModeSelectorContext::ModeSelectorContext(ModeSelectorCanvas *canvas, wxGLContext *sharedContext) : PrimaryGLContext(canvas, sharedContext) { glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); } void ModeSelectorContext::DrawBegin() { glClearColor(ThemeMgr::mgr.currentTheme->generalBackground.r, ThemeMgr::mgr.currentTheme->generalBackground.g, ThemeMgr::mgr.currentTheme->generalBackground.b,1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_TEXTURE_2D); } void ModeSelectorContext::DrawSelector(std::string label, int c, int cMax, bool on, float r, float g, float b, float a, float px, float py) { GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float viewWidth = (float) vp[2]; int fontSize = 18; if (viewWidth < 30 || viewHeight < 200) { fontSize = 16; } glColor4f(r, g, b, a); float y = 1.0 - ((float) (c+1) / (float) cMax * 2.0); float height = (2.0 / (float) cMax); float padX = (px / viewWidth); float padY = (py / viewHeight); if (a < 1.0) { glEnable(GL_BLEND); } glBegin(on?GL_QUADS:GL_LINE_LOOP); glVertex2f(-1.0 + padX, y + padY); glVertex2f(1.0 - padX, y + padY); glVertex2f(1.0 - padX, y + height - padY); glVertex2f(-1.0 + padX, y + height - padY); glEnd(); if (a < 1.0) { glDisable(GL_BLEND); } if (on) { glColor4f(0, 0, 0, a); } //Do not zoom the selectors GLFont::getFont(fontSize).drawString(label, 0.0, y + height / 2.0, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); } void ModeSelectorContext::DrawEnd() { // glFlush(); // CheckGLError(); } CubicSDR-0.2.3/src/visual/ModeSelectorContext.h000066400000000000000000000010041322677621400213140ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "PrimaryGLContext.h" #include "Gradient.h" #define NUM_WATERFALL_LINES 512 class ModeSelectorCanvas; class ModeSelectorContext: public PrimaryGLContext { public: ModeSelectorContext(ModeSelectorCanvas *canvas, wxGLContext *sharedContext); void DrawBegin(); void DrawSelector(std::string label, int c, int cMax, bool on, float r, float g, float b, float a, float padx, float pady); void DrawEnd(); }; CubicSDR-0.2.3/src/visual/PrimaryGLContext.cpp000066400000000000000000000400651322677621400211420ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "PrimaryGLContext.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include wxString PrimaryGLContext::glGetwxString(GLenum name) { const GLubyte *v = glGetString(name); if (v == 0) { // The error is not important. It is GL_INVALID_ENUM. // We just want to clear the error stack. glGetError(); return wxString(); } return wxString((const char*) v); } void PrimaryGLContext::CheckGLError() { GLenum errLast = GL_NO_ERROR; for (;;) { GLenum err = glGetError(); if (err == GL_NO_ERROR) return; if (err == errLast) { std::cout << "OpenGL error state couldn't be reset." << std::endl; return; } errLast = err; std::cout << "OpenGL Error " << err << std::endl; } } PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas, wxGLContext *sharedContext) : wxGLContext(canvas, sharedContext), hoverAlpha(1.0) { //#ifndef __linux__ // SetCurrent(*canvas); // // Pre-load fonts // for (int i = 0; i < GLFONT_MAX; i++) { // getFont((GLFontSize) i); // } // CheckGLError(); //#endif } void PrimaryGLContext::DrawDemodInfo(DemodulatorInstancePtr demod, RGBA4f color, long long center_freq, long long srate, bool centerline) { if (!demod) { return; } if (!srate) { srate = wxGetApp().getSampleRate(); } GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float viewWidth = (float) vp[2]; if (center_freq == -1) { center_freq = wxGetApp().getFrequency(); } long long demodFreq = demod->getFrequency(); if (demod->isDeltaLock()) { demodFreq = wxGetApp().getFrequency() + demod->getDeltaLockOfs(); } float uxPos = (float) (demodFreq - (center_freq - srate / 2)) / (float) srate; uxPos = (uxPos - 0.5) * 2.0; glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(color.r, color.g, color.b, 0.6f); float ofs = ((float) demod->getBandwidth()) / (float) srate; float ofsLeft = (demod->getDemodulatorType()!="USB")?ofs:0, ofsRight = (demod->getDemodulatorType()!="LSB")?ofs:0; float labelHeight = 20.0 / viewHeight; float hPos = -1.0 + labelHeight; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); bool soloMode = wxGetApp().getSoloMode(); bool isRecording = demod->isRecording(); bool isSolo = soloMode && demod == wxGetApp().getDemodMgr().getLastActiveDemodulator(); RGBA4f labelBg(0, 0, 0, 0.35f); if (isSolo) { labelBg.r = labelBg.g = 0.8f; } else if (demod->isMuted()) { labelBg.r = 0.8f; } else if (soloMode) { labelBg.r = 0.2f; } // TODO: Better recording indicator... pulsating red circle? if (isRecording) { auto t = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); labelBg.g = sinf(2.0f * M_PI * (float(t) / 1000.0f)) * 0.25f + 0.75f; } glColor4f(labelBg.r, labelBg.g, labelBg.b, labelBg.a); glBegin(GL_QUADS); glVertex3f(uxPos - ofsLeft, hPos + labelHeight, 0.0); glVertex3f(uxPos - ofsLeft, -1.0, 0.0); glVertex3f(uxPos + ofsRight, -1.0, 0.0); glVertex3f(uxPos + ofsRight, hPos + labelHeight, 0.0); glEnd(); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glColor4f(color.r, color.g, color.b, 0.2f); glBegin(GL_QUADS); glVertex3f(uxPos - ofsLeft, 1.0, 0.0); glVertex3f(uxPos - ofsLeft, -1.0, 0.0); glVertex3f(uxPos + ofsRight, -1.0, 0.0); glVertex3f(uxPos + ofsRight, 1.0, 0.0); glEnd(); if (ofs * 2.0 < 16.0 / viewWidth) { glColor4f(color.r, color.g, color.b, 0.2f); glBegin(GL_QUADS); glVertex3f(uxPos - ofsLeft, hPos + labelHeight, 0.0); glVertex3f(uxPos - ofsLeft, -1.0, 0.0); glVertex3f(uxPos + ofsRight, -1.0, 0.0); glVertex3f(uxPos + ofsRight, hPos + labelHeight, 0.0); glEnd(); } if (centerline) { glColor4f(color.r, color.g, color.b, 0.5); glBegin(GL_LINES); glVertex3f(uxPos, 1.0, 0.0); glVertex3f(uxPos, -1.0, 0.0); glEnd(); } glColor4f(1.0, 1.0, 1.0, 0.8f); std::string demodLabel, demodPrefix; if (demod->isDeltaLock()) { demodPrefix.append("V"); } if (isRecording) { demodPrefix.append("R"); } if (demod->isMuted()) { demodPrefix.append("M"); } else if (isSolo) { demodPrefix.append("S"); } // Set the prefix if (!demodPrefix.empty()) { demodLabel = "[" + demodPrefix + "] "; } // Append the default label demodLabel.append(demod->getLabel()); if (demod->getDemodulatorType() == "USB") { GLFont::getFont(16, GLFont::getScaleFactor()).drawString(demodLabel, uxPos, hPos, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); } else if (demod->getDemodulatorType() == "LSB") { GLFont::getFont(16, GLFont::getScaleFactor()).drawString(demodLabel, uxPos, hPos, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); } else { GLFont::getFont(16, GLFont::getScaleFactor()).drawString(demodLabel, uxPos, hPos, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); } glDisable(GL_BLEND); } void PrimaryGLContext::DrawFreqBwInfo(long long freq, int bw, RGBA4f color, long long center_freq, long long srate, bool stack, bool centerline) { if (!srate) { srate = wxGetApp().getSampleRate(); } GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float viewWidth = (float) vp[2]; if (center_freq == -1) { center_freq = wxGetApp().getFrequency(); } float uxPos = (float) (freq - (center_freq - srate / 2)) / (float) srate; uxPos = (uxPos - 0.5) * 2.0; std::string lastType = wxGetApp().getDemodMgr().getLastDemodulatorType(); float ofs = (float) bw / (float) srate; float ofsLeft = (lastType!="USB")?ofs:0, ofsRight = (lastType!="LSB")?ofs:0; float labelHeight = 20.0 / viewHeight; float hPos = -1.0 + (stack?(labelHeight*3.0):labelHeight); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(0, 0, 0, 0.35f); glBegin(GL_QUADS); glVertex3f(uxPos - ofsLeft, hPos + labelHeight, 0.0); glVertex3f(uxPos - ofsLeft, -1.0, 0.0); glVertex3f(uxPos + ofsRight, -1.0, 0.0); glVertex3f(uxPos + ofsRight, hPos + labelHeight, 0.0); glEnd(); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glColor4f(color.r, color.g, color.b, 0.1f); glBegin(GL_QUADS); glVertex3f(uxPos - ofsLeft, 1.0, 0.0); glVertex3f(uxPos - ofsLeft, -1.0, 0.0); glVertex3f(uxPos + ofsRight, -1.0, 0.0); glVertex3f(uxPos + ofsRight, 1.0, 0.0); glEnd(); if (ofs * 2.0 < 16.0 / viewWidth) { glColor4f(color.r, color.g, color.b, 0.1f); glBegin(GL_QUADS); glVertex3f(uxPos - ofsLeft, hPos + labelHeight, 0.0); glVertex3f(uxPos - ofsLeft, -1.0, 0.0); glVertex3f(uxPos + ofsRight, -1.0, 0.0); glVertex3f(uxPos + ofsRight, hPos + labelHeight, 0.0); glEnd(); } if (centerline) { glColor4f(color.r, color.g, color.b, 0.5); glBegin(GL_LINES); glVertex3f(uxPos, 1.0, 0.0); glVertex3f(uxPos, -1.0, 0.0); glEnd(); } glColor4f(1.0, 1.0, 1.0, 0.8f); std::string demodLabel = std::to_string((double)freq/1000000.0); double shadowOfsX = 4.0 / viewWidth, shadowOfsY = 2.0 / viewHeight; GLFont::Drawer refDrawingFont = GLFont::getFont(16, GLFont::getScaleFactor()); if (lastType == "USB") { glColor4f(0,0,0, 1.0); glBlendFunc(GL_ONE, GL_ZERO); refDrawingFont.drawString(demodLabel, uxPos+shadowOfsX, hPos+shadowOfsY, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); refDrawingFont.drawString(demodLabel, uxPos-shadowOfsX, hPos-shadowOfsY, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); glColor4f(color.r, color.g, color.b, 1.0); glBlendFunc(GL_SRC_ALPHA, GL_ONE); refDrawingFont.drawString(demodLabel, uxPos, hPos, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); } else if (lastType == "LSB") { glBlendFunc(GL_ONE, GL_ZERO); glColor4f(0,0,0, 1.0); refDrawingFont.drawString(demodLabel, uxPos+shadowOfsX, hPos+shadowOfsY, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); refDrawingFont.drawString(demodLabel, uxPos-shadowOfsX, hPos-shadowOfsY, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); glColor4f(color.r, color.g, color.b, 1.0); glBlendFunc(GL_SRC_ALPHA, GL_ONE); refDrawingFont.drawString(demodLabel, uxPos, hPos, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); } else { glBlendFunc(GL_ONE, GL_ZERO); glColor4f(0,0,0, 1.0); glBlendFunc(GL_SRC_ALPHA, GL_ONE); refDrawingFont.drawString(demodLabel, uxPos+shadowOfsX, hPos+shadowOfsY, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); refDrawingFont.drawString(demodLabel, uxPos-shadowOfsX, hPos-shadowOfsY, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); glColor4f(color.r, color.g, color.b, 1.0); refDrawingFont.drawString(demodLabel, uxPos, hPos, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); } glDisable(GL_BLEND); } void PrimaryGLContext::DrawDemod(DemodulatorInstancePtr demod, RGBA4f color, long long center_freq, long long srate) { if (!demod) { return; } if (!srate) { srate = wxGetApp().getSampleRate(); } if (center_freq == -1) { center_freq = wxGetApp().getFrequency(); } long long demodFreq = demod->getFrequency(); if (demod->isDeltaLock()) { demodFreq = wxGetApp().getFrequency() + demod->getDeltaLockOfs(); } float uxPos = (float) (demodFreq - (center_freq - srate / 2)) / (float) srate; glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glColor4f(color.r, color.g, color.b, 0.6f); float ofs = ((float) demod->getBandwidth()) / (float) srate; float ofsLeft = (demod->getDemodulatorType()!="USB")?ofs:0, ofsRight = (demod->getDemodulatorType()!="LSB")?ofs:0; glBegin(GL_LINES); glVertex3f((uxPos - 0.5) * 2.0, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0, -1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 - ofsLeft, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 - ofsLeft, -1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 + ofsRight, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 + ofsRight, -1.0, 0.0); glEnd(); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glColor4f(color.r, color.g, color.b, 0.2*hoverAlpha); glBegin(GL_QUADS); glVertex3f((uxPos - 0.5) * 2.0 - ofsLeft, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 - ofsLeft, -1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 + ofsRight, -1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 + ofsRight, 1.0, 0.0); glEnd(); GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float viewWidth = (float) vp[2]; float labelHeight = 20.0 / viewHeight; float xOfs = (2.0 / viewWidth); float yOfs = (2.0 / viewHeight); float hPos = labelHeight; glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_COLOR_MATERIAL); glEnable(GL_BLEND); //Displayed string is wstring, so use wxString to do the heavy lifting of converting getDemodulatorType()... wxString demodStr; demodStr.assign(demod->getDemodulatorType()); if (demodStr == "LSB") { uxPos -= xOfs; } else if (demodStr == "USB") { uxPos += xOfs; } // advanced demodulators start here // if (demod->getDemodulatorCons() > 0) { // demodStr = demodStr + std::to_string(demod->getDemodulatorCons()); // } // add lock to string if we have an lock if(demod->getDemodulatorLock()) { demodStr += " Lock"; } // else { // demodStr = demodStr + " UnLock"; // } //Shift the user label from the modem label more for the bigger //font sizes so they do not step on each other... double heightShift = GLFont::getScaleFactor(); //demodulator user label if present: type is displayed above the label, which is at the bottom of the screen. if (!demod->getDemodulatorUserLabel().empty()) { drawSingleDemodLabel(demodStr.ToStdWstring(), uxPos, hPos * 1.2 + hPos * 1.2 * heightShift, xOfs, yOfs, GLFont::GLFONT_ALIGN_CENTER); drawSingleDemodLabel(demod->getDemodulatorUserLabel(), uxPos, hPos * 1.2, xOfs, yOfs, GLFont::GLFONT_ALIGN_CENTER); } else { drawSingleDemodLabel(demodStr.ToStdWstring(), uxPos, hPos * 1.2, xOfs, yOfs, GLFont::GLFONT_ALIGN_CENTER); } glDisable(GL_BLEND); } void PrimaryGLContext::drawSingleDemodLabel(const std::wstring& demodStr, float uxPos, float hPos, float xOfs, float yOfs, GLFont::Align demodAlign) { GLFont::Drawer refDrawingFont = GLFont::getFont(16, GLFont::getScaleFactor()); glColor3f(0, 0, 0); refDrawingFont.drawString(demodStr, 2.0 * (uxPos - 0.5) + xOfs, -1.0 + hPos - yOfs, demodAlign, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); glColor3f(1, 1, 1); refDrawingFont.drawString(demodStr, 2.0 * (uxPos - 0.5), -1.0 + hPos, demodAlign, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); } void PrimaryGLContext::DrawFreqSelector(float uxPos, RGBA4f color, float w, long long /* center_freq */, long long srate) { DemodulatorInstancePtr demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); long long bw = 0; std::string last_type = wxGetApp().getDemodMgr().getLastDemodulatorType(); if (!demod) { bw = wxGetApp().getDemodMgr().getLastBandwidth(); } else { bw = demod->getBandwidth(); } if (!srate) { srate = wxGetApp().getSampleRate(); } glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glColor4f(color.r, color.g, color.b, 0.6f); glBegin(GL_LINES); glVertex3f((uxPos - 0.5) * 2.0, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0, -1.0, 0.0); float ofs; if (w) { ofs = w; } else { ofs = ((float) bw) / (float) srate; } if (last_type != "USB") { glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 - ofs, -1.0, 0.0); } if (last_type != "LSB") { glVertex3f((uxPos - 0.5) * 2.0 + ofs, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 + ofs, -1.0, 0.0); } glEnd(); glDisable(GL_BLEND); } void PrimaryGLContext::DrawRangeSelector(float uxPos1, float uxPos2, RGBA4f color) { if (uxPos2 < uxPos1) { float temp = uxPos2; uxPos2=uxPos1; uxPos1=temp; } std::string last_type = wxGetApp().getDemodMgr().getLastDemodulatorType(); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glColor4f(color.r, color.g, color.b, 0.6f); glLineWidth((last_type == "USB")?2.0:1.0); glBegin(GL_LINES); glVertex3f((uxPos1 - 0.5) * 2.0, 1.0, 0.0); glVertex3f((uxPos1 - 0.5) * 2.0, -1.0, 0.0); glEnd(); glLineWidth((last_type == "LSB")?2.0:1.0); glBegin(GL_LINES); glVertex3f((uxPos2 - 0.5) * 2.0, 1.0, 0.0); glVertex3f((uxPos2 - 0.5) * 2.0, -1.0, 0.0); glEnd(); glLineWidth(1.0); glDisable(GL_BLEND); } void PrimaryGLContext::BeginDraw(float r, float g, float b) { glClearColor(r,g,b, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void PrimaryGLContext::EndDraw() { // glFlush(); // CheckGLError(); } void PrimaryGLContext::setHoverAlpha(float hoverAlpha) { this->hoverAlpha = hoverAlpha; } CubicSDR-0.2.3/src/visual/PrimaryGLContext.h000066400000000000000000000025211322677621400206020ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include #include #include "CubicSDRDefs.h" #include "GLFont.h" #include "DemodulatorMgr.h" #include "ColorTheme.h" class PrimaryGLContext: public wxGLContext { public: PrimaryGLContext(wxGLCanvas *canvas, wxGLContext *sharedContext); static wxString glGetwxString(GLenum name); static void CheckGLError(); void BeginDraw(float r, float g, float b); void EndDraw(); void DrawFreqSelector(float uxPos, RGBA4f color, float w = 0, long long center_freq = -1, long long srate = 0); void DrawRangeSelector(float uxPos1, float uxPos2, RGBA4f color); void DrawDemod(DemodulatorInstancePtr demod, RGBA4f color, long long center_freq = -1, long long srate = 0); void DrawDemodInfo(DemodulatorInstancePtr demod, RGBA4f color, long long center_freq = -1, long long srate = 0, bool centerline = false); void DrawFreqBwInfo(long long freq, int bw, RGBA4f color, long long center_freq = - 1, long long srate = 0, bool stack = false, bool centerline = false); void setHoverAlpha(float hoverAlpha); private: float hoverAlpha; void drawSingleDemodLabel(const std::wstring& demodStr, float uxPos, float hPos, float xOfs, float yOfs, GLFont::Align demodAlign); }; CubicSDR-0.2.3/src/visual/ScopeCanvas.cpp000066400000000000000000000174531322677621400201410ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ScopeCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include #include wxBEGIN_EVENT_TABLE(ScopeCanvas, wxGLCanvas) EVT_PAINT(ScopeCanvas::OnPaint) EVT_IDLE(ScopeCanvas::OnIdle) EVT_MOTION(ScopeCanvas::OnMouseMoved) EVT_LEFT_DOWN(ScopeCanvas::OnMouseDown) EVT_LEFT_UP(ScopeCanvas::OnMouseReleased) EVT_RIGHT_DOWN(ScopeCanvas::OnMouseRightDown) EVT_RIGHT_UP(ScopeCanvas::OnMouseRightReleased) EVT_LEAVE_WINDOW(ScopeCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(ScopeCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() ScopeCanvas::ScopeCanvas(wxWindow *parent, std::vector dispAttrs) : InteractiveCanvas(parent, dispAttrs), ppmMode(false), ctr(0), ctrTarget(0), dragAccel(0), helpTip("") { glContext = new ScopeContext(this, &wxGetApp().GetContext(this)); inputData->set_max_num_items(2); bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_Y); bgPanel.setSize(1.0, 0.5f); bgPanel.setPosition(0.0, -0.5f); panelSpacing = 0.4f; parentPanel.addChild(&scopePanel); parentPanel.addChild(&spectrumPanel); parentPanel.setFill(GLPanel::GLPANEL_FILL_NONE); scopePanel.setSize(1.0,-1.0); spectrumPanel.setSize(1.0,-1.0); showDb = true; spectrumPanel.setShowDb(showDb); //dB offset is a RF value, has no meaning in audio, disable it. spectrumPanel.setUseDBOffset(false); } ScopeCanvas::~ScopeCanvas() { } bool ScopeCanvas::scopeVisible() { float panelInterval = (2.0 + panelSpacing); ctrTarget = abs(round(ctr / panelInterval)); if (ctrTarget == 0 || dragAccel || (ctr != ctrTarget)) { return true; } return false; } bool ScopeCanvas::spectrumVisible() { float panelInterval = (2.0 + panelSpacing); ctrTarget = abs(round(ctr / panelInterval)); if (ctrTarget == 1 || dragAccel || (ctr != ctrTarget)) { return true; } return false; } void ScopeCanvas::setDeviceName(std::string device_name) { deviceName = device_name; deviceName.append(" "); } void ScopeCanvas::setPPMMode(bool ppmMode) { this->ppmMode = ppmMode; } bool ScopeCanvas::getPPMMode() { return ppmMode; } void ScopeCanvas::setShowDb(bool show) { this->showDb = show; } bool ScopeCanvas::getShowDb() { return showDb; } void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); ScopeRenderDataPtr avData; while (inputData->try_pop(avData)) { if (!avData->spectrum) { scopePanel.setMode(avData->mode); if (avData->waveform_points.size()) { scopePanel.setPoints(avData->waveform_points); } } else { if (avData->waveform_points.size()) { spectrumPanel.setPoints(avData->waveform_points); spectrumPanel.setFloorValue(avData->fft_floor); spectrumPanel.setCeilValue(avData->fft_ceil); spectrumPanel.setBandwidth((avData->sampleRate/2)*1000); spectrumPanel.setFreq((avData->sampleRate/4)*1000); spectrumPanel.setFFTSize(avData->fft_size); spectrumPanel.setShowDb(showDb); } } } glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); // TODO: find out why frontbuffer drawing has stopped working in wx 3.1.0? // if (scopePanel.getMode() == ScopePanel::SCOPE_MODE_XY && !spectrumVisible()) { // glDrawBuffer(GL_FRONT); // glContext->DrawBegin(false); // } else { // glDrawBuffer(GL_BACK); glContext->DrawBegin(); bgPanel.setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground * 3.0, RGBA4f(0,0,0,1)); bgPanel.calcTransform(CubicVR::mat4::identity()); bgPanel.draw(); // } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glLoadMatrixf(CubicVR::mat4::perspective(45.0, 1.0, 1.0, 1000.0).to_ptr()); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); CubicVR::mat4 modelView = CubicVR::mat4::lookat(0, 0, -1.205f, 0, 0, 0, 0, -1, 0); float panelWidth = 1.0; float panelInterval = (panelWidth * 2.0 + panelSpacing); if (!mouseTracker.mouseDown()) { ctrTarget = round(ctr / panelInterval); if (ctrTarget < -1.0) { ctrTarget = -1.0; } else if (ctrTarget > 0.0) { ctrTarget = 0.0; } ctrTarget *= panelInterval; if (!dragAccel) { if (ctr != ctrTarget) { ctr += (ctrTarget-ctr)*0.2; } if (abs(ctr - ctrTarget) < 0.001) { ctr=ctrTarget; } } else { dragAccel -= dragAccel * 0.1; if ((abs(dragAccel) < 0.2) || (ctr < (ctrTarget-panelInterval/2.0)) || (ctr > (ctrTarget+panelInterval/2.0)) ) { dragAccel = 0; } else { ctr += dragAccel; } } } float roty = 0; scopePanel.setPosition(ctr, 0); if (scopeVisible()) { scopePanel.contentsVisible = true; roty = atan2(scopePanel.pos[0],1.2); scopePanel.rot[1] = -(roty * (180.0 / M_PI)); } else { scopePanel.contentsVisible = false; } spectrumPanel.setPosition(panelInterval+ctr, 0); if (spectrumVisible()) { spectrumPanel.setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground * 2.0, RGBA4f(0,0,0,1)); spectrumPanel.contentsVisible = true; roty = atan2(spectrumPanel.pos[0],1.2); spectrumPanel.rot[1] = -(roty * (180.0 / M_PI)); } else { spectrumPanel.contentsVisible = false; } parentPanel.calcTransform(modelView); parentPanel.draw(); if (spectrumVisible()) { spectrumPanel.drawChildren(); } glLoadMatrixf(scopePanel.transform.to_ptr()); if (!deviceName.empty()) { glContext->DrawDeviceName(deviceName); } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glContext->DrawTunerTitles(ppmMode); glContext->DrawEnd(); // if (scopePanel.getMode() != ScopePanel::SCOPE_MODE_XY || spectrumVisible()) { SwapBuffers(); // } } void ScopeCanvas::OnIdle(wxIdleEvent &event) { Refresh(); event.RequestMore(); } ScopeRenderDataQueuePtr ScopeCanvas::getInputQueue() { return inputData; } void ScopeCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); if (mouseTracker.mouseDown()) { dragAccel = 4.0*mouseTracker.getDeltaMouseX(); ctr += dragAccel; } } void ScopeCanvas::OnMouseWheelMoved(wxMouseEvent& /* event */) { } void ScopeCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); } void ScopeCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); } void ScopeCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseEnterWindow(event); if (!helpTip.empty()) { setStatusText(helpTip); } SetCursor(wxCURSOR_SIZEWE); } void ScopeCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); } void ScopeCanvas::setHelpTip(std::string tip) { helpTip = tip; } void ScopeCanvas::OnKeyDown(wxKeyEvent& event) { InteractiveCanvas::OnKeyDown(event); switch (event.GetKeyCode()) { case 'B': setShowDb(!getShowDb()); break; default: event.Skip(); } } void ScopeCanvas::OnKeyUp(wxKeyEvent& event) { InteractiveCanvas::OnKeyUp(event); } CubicSDR-0.2.3/src/visual/ScopeCanvas.h000066400000000000000000000036611322677621400176020ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include #include #include #include "ScopeContext.h" #include "ScopeVisualProcessor.h" #include "ScopePanel.h" #include "SpectrumPanel.h" #include "InteractiveCanvas.h" class ScopeCanvas: public InteractiveCanvas { public: ScopeCanvas(wxWindow *parent, std::vector dispAttrs); ~ScopeCanvas(); //This is public because it is indeed forwarded from //AppFrame::OnGlobalKeyDown, because global key handler intercepts //calls in all windows. void OnKeyDown(wxKeyEvent& event); //This is public because it is indeed forwarded from //AppFrame::OnGlobalKeyUp, because global key handler intercepts //calls in all windows. void OnKeyUp(wxKeyEvent& event); void setDeviceName(std::string device_name); void setPPMMode(bool ppmMode); bool getPPMMode(); void setShowDb(bool showDb); bool getShowDb(); bool scopeVisible(); bool spectrumVisible(); void setHelpTip(std::string tip); ScopeRenderDataQueuePtr getInputQueue(); private: void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); void OnMouseMoved(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); ScopeRenderDataQueuePtr inputData = std::make_shared(); ScopePanel scopePanel; GLPanel parentPanel; SpectrumPanel spectrumPanel; GLPanel bgPanel; ScopeContext *glContext; std::string deviceName; bool ppmMode; bool showDb; float panelSpacing; float ctr; float ctrTarget; float dragAccel; std::string helpTip; // event table wxDECLARE_EVENT_TABLE(); }; CubicSDR-0.2.3/src/visual/ScopeContext.cpp000066400000000000000000000045021322677621400203410ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "ScopeContext.h" #include "ScopeCanvas.h" #include "ColorTheme.h" ScopeContext::ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext) : PrimaryGLContext(canvas, sharedContext) { glDisable (GL_CULL_FACE); glDisable (GL_DEPTH_TEST); glMatrixMode (GL_PROJECTION); glLoadIdentity(); } void ScopeContext::DrawBegin(bool clear) { if (clear) { glClearColor(ThemeMgr::mgr.currentTheme->scopeBackground.r, ThemeMgr::mgr.currentTheme->scopeBackground.g, ThemeMgr::mgr.currentTheme->scopeBackground.b, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } glMatrixMode (GL_MODELVIEW); glLoadIdentity(); glDisable (GL_TEXTURE_2D); } void ScopeContext::DrawTunerTitles(bool ppmMode) { glLoadIdentity(); GLint vp[4]; glGetIntegerv(GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float hPos = (float) (13) / viewHeight; glColor3f(0.65f, 0.65f, 0.65f); GLFont::Drawer refDrawingFont = GLFont::getFont(12, GLFont::getScaleFactor()); //better position frequency/bandwith labels according to font scale double shiftFactor = GLFont::getScaleFactor()+0.5; refDrawingFont.drawString(ppmMode?"Device PPM":"Frequency", -0.66f, -1.0 +hPos*shiftFactor, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); refDrawingFont.drawString("Bandwidth", 0.0, -1.0 +hPos*shiftFactor, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); refDrawingFont.drawString("Center Frequency", 0.66f, -1.0 +hPos*shiftFactor, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); } void ScopeContext::DrawDeviceName(std::string deviceName) { GLint vp[4]; glGetIntegerv(GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float hPos = (float) (viewHeight - 20) / viewHeight; glColor3f(0.65f, 0.65f, 0.65f); GLFont::getFont(12, GLFont::getScaleFactor()).drawString(deviceName.c_str(), 1.0, hPos, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true); } void ScopeContext::DrawEnd() { // glFlush(); // CheckGLError(); } void ScopeContext::DrawDivider() { glColor3f(1.0, 1.0, 1.0); glBegin (GL_LINES); glVertex2f(0.0, -1.0); glVertex2f(0.0, 1.0); glEnd(); } CubicSDR-0.2.3/src/visual/ScopeContext.h000066400000000000000000000007701322677621400200110ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "PrimaryGLContext.h" #include "Gradient.h" #define NUM_WATERFALL_LINES 512 class ScopeCanvas; class ScopeContext: public PrimaryGLContext { public: ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext); void DrawBegin(bool clear=true); void DrawTunerTitles(bool ppmMode=false); void DrawDeviceName(std::string deviceName); void DrawDivider(); void DrawEnd(); private: }; CubicSDR-0.2.3/src/visual/SpectrumCanvas.cpp000066400000000000000000000231051322677621400206610ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "SpectrumCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include #include #include "WaterfallCanvas.h" wxBEGIN_EVENT_TABLE(SpectrumCanvas, wxGLCanvas) EVT_PAINT(SpectrumCanvas::OnPaint) EVT_IDLE(SpectrumCanvas::OnIdle) EVT_MOTION(SpectrumCanvas::OnMouseMoved) EVT_LEFT_DOWN(SpectrumCanvas::OnMouseDown) EVT_LEFT_UP(SpectrumCanvas::OnMouseReleased) EVT_ENTER_WINDOW(SpectrumCanvas::OnMouseEnterWindow) EVT_LEAVE_WINDOW(SpectrumCanvas::OnMouseLeftWindow) EVT_MOUSEWHEEL(SpectrumCanvas::OnMouseWheelMoved) EVT_RIGHT_DOWN(SpectrumCanvas::OnMouseRightDown) EVT_RIGHT_UP(SpectrumCanvas::OnMouseRightReleased) wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, std::vector dispAttrs) : InteractiveCanvas(parent, dispAttrs), waterfallCanvas(NULL) { glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); visualDataQueue->set_max_num_items(1); SetCursor(wxCURSOR_SIZEWE); scaleFactor = 1.0; resetScaleFactor = false; scaleFactorEnabled = false; bwChange = 0.0; } SpectrumCanvas::~SpectrumCanvas() { } void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); SpectrumVisualDataPtr vData; if (visualDataQueue->try_pop(vData)) { if (vData) { spectrumPanel.setPoints(vData->spectrum_points); spectrumPanel.setPeakPoints(vData->spectrum_hold_points); spectrumPanel.setFloorValue(vData->fft_floor); spectrumPanel.setCeilValue(vData->fft_ceiling); } } if (resetScaleFactor) { scaleFactor += (1.0-scaleFactor)*0.05; if (fabs(scaleFactor-1.0) < 0.01) { scaleFactor = 1.0; resetScaleFactor = false; } updateScaleFactor(scaleFactor); } glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); glContext->BeginDraw(0,0,0); spectrumPanel.setFreq(getCenterFrequency()); spectrumPanel.setBandwidth(getBandwidth()); spectrumPanel.calcTransform(CubicVR::mat4::identity()); spectrumPanel.draw(); glLoadIdentity(); auto demods = wxGetApp().getDemodMgr().getDemodulators(); auto activeDemodulator = wxGetApp().getDemodMgr().getActiveDemodulator(); for (int i = 0, iMax = demods.size(); i < iMax; i++) { if (!demods[i]->isActive()) { continue; } glContext->DrawDemodInfo(demods[i], ThemeMgr::mgr.currentTheme->fftHighlight, getCenterFrequency(), getBandwidth(), activeDemodulator==demods[i]); } if (waterfallCanvas && !activeDemodulator) { MouseTracker *wfmt = waterfallCanvas->getMouseTracker(); if (wfmt->mouseInView()) { int snap = wxGetApp().getFrequencySnap(); long long freq = getFrequencyAt(wfmt->getMouseX()); if (snap > 1) { freq = roundf((float)freq/(float)snap)*snap; } auto lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator(); bool isNew = (((waterfallCanvas->isShiftDown() || (lastActiveDemodulator && !lastActiveDemodulator->isActive())) && lastActiveDemodulator) || (!lastActiveDemodulator)); glContext->DrawFreqBwInfo(freq, wxGetApp().getDemodMgr().getLastBandwidth(), isNew?ThemeMgr::mgr.currentTheme->waterfallNew:ThemeMgr::mgr.currentTheme->waterfallHover, getCenterFrequency(), getBandwidth(), true, true); } } glContext->EndDraw(); spectrumPanel.drawChildren(); SwapBuffers(); } void SpectrumCanvas::OnIdle(wxIdleEvent &event) { Refresh(); event.RequestMore(); } void SpectrumCanvas::moveCenterFrequency(long long freqChange) { long long freq = wxGetApp().getFrequency(); if (isView) { if (centerFreq - freqChange < bandwidth/2) { centerFreq = bandwidth/2; } else { centerFreq -= freqChange; } if (waterfallCanvas) { waterfallCanvas->setCenterFrequency(centerFreq); } long long bwOfs = (centerFreq > freq) ? ((long long) bandwidth / 2) : (-(long long) bandwidth / 2); long long freqEdge = centerFreq + bwOfs; if (abs(freq - freqEdge) > (wxGetApp().getSampleRate() / 2)) { freqChange = -((centerFreq > freq) ? (freqEdge - freq - (wxGetApp().getSampleRate() / 2)) : (freqEdge - freq + (wxGetApp().getSampleRate() / 2))); } else { freqChange = 0; } } if (freqChange) { if (freq - freqChange < wxGetApp().getSampleRate()/2) { freq = wxGetApp().getSampleRate()/2; } else { freq -= freqChange; } wxGetApp().setFrequency(freq); } } void SpectrumCanvas::setShowDb(bool showDb) { spectrumPanel.setShowDb(showDb); } bool SpectrumCanvas::getShowDb() { return spectrumPanel.getShowDb(); } void SpectrumCanvas::setUseDBOfs(bool showDb) { spectrumPanel.setUseDBOffset(showDb); } bool SpectrumCanvas::getUseDBOfs() { return spectrumPanel.getUseDBOffset(); } void SpectrumCanvas::setView(long long center_freq_in, int bandwidth_in) { bwChange += bandwidth_in-bandwidth; #define BW_RESET_TH 400000 if (bwChange > BW_RESET_TH || bwChange < -BW_RESET_TH) { resetScaleFactor = true; bwChange = 0; } InteractiveCanvas::setView(center_freq_in, bandwidth_in); } void SpectrumCanvas::disableView() { InteractiveCanvas::disableView(); } void SpectrumCanvas::setScaleFactorEnabled(bool en) { scaleFactorEnabled = en; } void SpectrumCanvas::setFFTSize(int fftSize) { spectrumPanel.setFFTSize(fftSize); } void SpectrumCanvas::updateScaleFactor(float factor) { SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor(); FFTVisualDataThread *wdt = wxGetApp().getAppFrame()->getWaterfallDataThread(); SpectrumVisualProcessor *wp = wdt->getProcessor(); scaleFactor = factor; sp->setScaleFactor(factor); wp->setScaleFactor(factor); } void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); if (mouseTracker.mouseDown()) { int freqChange = mouseTracker.getDeltaMouseX() * getBandwidth(); if (freqChange != 0) { moveCenterFrequency(freqChange); } } else if (scaleFactorEnabled && mouseTracker.mouseRightDown()) { float yDelta = mouseTracker.getDeltaMouseY(); scaleFactor += yDelta*2.0; if (scaleFactor < 0.25) { scaleFactor = 0.25; } if (scaleFactor > 10.0) { scaleFactor = 10.0; } resetScaleFactor = false; updateScaleFactor(scaleFactor); } else { if (scaleFactorEnabled) { setStatusText("Drag horizontal to adjust center frequency. Right-drag or SHIFT+UP/DOWN to adjust vertical scale; right-click to reset. 'B' to toggle decibels display."); } else { setStatusText("Displaying spectrum of active demodulator."); } } } void SpectrumCanvas::OnMouseDown(wxMouseEvent& event) { mouseTracker.setVertDragLock(true); InteractiveCanvas::OnMouseDown(event); SetCursor(wxCURSOR_CROSS); } void SpectrumCanvas::OnMouseWheelMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseWheelMoved(event); if (waterfallCanvas) { waterfallCanvas->OnMouseWheelMoved(event); } } void SpectrumCanvas::OnMouseReleased(wxMouseEvent& event) { mouseTracker.setVertDragLock(false); InteractiveCanvas::OnMouseReleased(event); SetCursor(wxCURSOR_SIZEWE); } void SpectrumCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseEnterWindow(event); SetCursor(wxCURSOR_SIZEWE); #ifdef _WIN32 if (wxGetApp().getAppFrame()->canFocus()) { this->SetFocus(); } #endif } void SpectrumCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor(wxCURSOR_SIZEWE); } void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) { waterfallCanvas = canvas_in; } SpectrumVisualDataQueuePtr SpectrumCanvas::getVisualDataQueue() { return visualDataQueue; } void SpectrumCanvas::OnMouseRightDown(wxMouseEvent& event) { mouseTracker.setHorizDragLock(true); mouseTracker.OnMouseRightDown(event); scaleFactor = wxGetApp().getSpectrumProcessor()->getScaleFactor(); } void SpectrumCanvas::OnMouseRightReleased(wxMouseEvent& event) { mouseTracker.setHorizDragLock(false); if (!mouseTracker.getOriginDeltaMouseY()) { resetScaleFactor = true; wxGetApp().getSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); //make the peak hold act on the current dmod also, like a zoomed-in version. if (wxGetApp().getDemodSpectrumProcessor()) { wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); } } mouseTracker.OnMouseRightReleased(event); } void SpectrumCanvas::OnKeyDown(wxKeyEvent& event) { InteractiveCanvas::OnKeyDown(event); switch (event.GetKeyCode()) { case 'B': setShowDb(!getShowDb()); break; default: event.Skip(); } } void SpectrumCanvas::OnKeyUp(wxKeyEvent& event) { InteractiveCanvas::OnKeyUp(event); } CubicSDR-0.2.3/src/visual/SpectrumCanvas.h000066400000000000000000000041761322677621400203350ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include #include #include #include "InteractiveCanvas.h" #include "PrimaryGLContext.h" #include "MouseTracker.h" #include "SpectrumVisualProcessor.h" #include "SpectrumPanel.h" class WaterfallCanvas; class SpectrumCanvas: public InteractiveCanvas { public: SpectrumCanvas(wxWindow *parent, std::vector dispAttrs); ~SpectrumCanvas(); //This is public because it is indeed forwarded from //AppFrame::OnGlobalKeyDown, because global key handler intercepts //calls in all windows. void OnKeyDown(wxKeyEvent& event); //This is public because it is indeed forwarded from //AppFrame::OnGlobalKeyUp, because global key handler intercepts //calls in all windows. void OnKeyUp(wxKeyEvent& event); void attachWaterfallCanvas(WaterfallCanvas *canvas_in); void moveCenterFrequency(long long freqChange); void setShowDb(bool showDb); bool getShowDb(); void setUseDBOfs(bool showDb); bool getUseDBOfs(); void setView(long long center_freq_in, int bandwidth_in); void disableView(); void setScaleFactorEnabled(bool en); void setFFTSize(int fftSize); SpectrumVisualDataQueuePtr getVisualDataQueue(); private: void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); void OnMouseMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); void OnMouseRightDown(wxMouseEvent& event); void OnMouseRightReleased(wxMouseEvent& event); void updateScaleFactor(float factor); PrimaryGLContext *glContext; WaterfallCanvas *waterfallCanvas; SpectrumPanel spectrumPanel; float scaleFactor; int bwChange; bool resetScaleFactor, scaleFactorEnabled; SpectrumVisualDataQueuePtr visualDataQueue = std::make_shared(); // event table wxDECLARE_EVENT_TABLE(); }; CubicSDR-0.2.3/src/visual/TuningCanvas.cpp000066400000000000000000000343521322677621400203310ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "TuningCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include wxBEGIN_EVENT_TABLE(TuningCanvas, wxGLCanvas) EVT_PAINT(TuningCanvas::OnPaint) EVT_IDLE(TuningCanvas::OnIdle) EVT_MOTION(TuningCanvas::OnMouseMoved) EVT_LEFT_DOWN(TuningCanvas::OnMouseDown) EVT_LEFT_UP(TuningCanvas::OnMouseReleased) EVT_RIGHT_DOWN(TuningCanvas::OnMouseRightDown) EVT_RIGHT_UP(TuningCanvas::OnMouseRightReleased) EVT_LEAVE_WINDOW(TuningCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(TuningCanvas::OnMouseEnterWindow) EVT_MOUSEWHEEL(TuningCanvas::OnMouseWheelMoved) //EVT_KEY_DOWN(TuningCanvas::OnKeyDown) //EVT_KEY_UP(TuningCanvas::OnKeyUp) wxEND_EVENT_TABLE() TuningCanvas::TuningCanvas(wxWindow *parent, std::vector dispAttrs) : InteractiveCanvas(parent, dispAttrs), dragAccum(0), uxDown(0), top(false), bottom(false), freq(-1), bw(-1), center(-1), halfBand(false) { glContext = new TuningContext(this, &wxGetApp().GetContext(this)); hoverIndex = 0; downIndex = 0; hoverState = TUNING_HOVER_NONE; downState = TUNING_HOVER_NONE; dragging = false; freqDP = -1.0; freqW = (1.0f / 3.0f) * 2.0f; bwDP = -1.0 + (2.25 / 3.0); bwW = (1.0 / 4.0) * 2.0; centerDP = -1.0f + (2.0f / 3.0f) * 2.0f; centerW = (1.0f / 3.0f) * 2.0f; currentPPM = lastPPM = 0; } TuningCanvas::~TuningCanvas() { } bool TuningCanvas::changed() { auto activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); long long current_freq = 0; if (activeDemod != nullptr) { freq = activeDemod->getFrequency(); } long long current_bw = wxGetApp().getDemodMgr().getLastBandwidth(); long long current_center = wxGetApp().getFrequency(); if (current_freq != freq || current_bw != bw || current_center != center) { return true; } return false; } void TuningCanvas::setHalfBand(bool hb) { halfBand = hb; Refresh(); } void TuningCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); glContext->DrawBegin(); auto activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); freq = 0; if (activeDemod != nullptr) { freq = activeDemod->getFrequency(); } bw = wxGetApp().getDemodMgr().getLastBandwidth(); center = wxGetApp().getFrequency(); if (mouseTracker.mouseDown()) { glContext->Draw(ThemeMgr::mgr.currentTheme->tuningBarDark.r, ThemeMgr::mgr.currentTheme->tuningBarDark.g, ThemeMgr::mgr.currentTheme->tuningBarDark.b, 0.75, mouseTracker.getOriginMouseX(), mouseTracker.getMouseX()); } RGBA4f clr = top ? ThemeMgr::mgr.currentTheme->tuningBarUp : ThemeMgr::mgr.currentTheme->tuningBarDown; RGBA4f clrDark = ThemeMgr::mgr.currentTheme->tuningBarDark; RGBA4f clrMid = ThemeMgr::mgr.currentTheme->tuningBarLight; glContext->DrawTunerBarIndexed(1, 3, 11, freqDP, freqW, clrMid, 0.25, true, true); // freq glContext->DrawTunerBarIndexed(4, 6, 11, freqDP, freqW, clrDark, 0.25, true, true); glContext->DrawTunerBarIndexed(7, 9, 11, freqDP, freqW, clrMid, 0.25, true, true); glContext->DrawTunerBarIndexed(10, 11, 11, freqDP, freqW, clrDark, 0.25, true, true); glContext->DrawTunerBarIndexed(1, 3, 7, bwDP, bwW, clrMid, 0.25, true, true); // bw glContext->DrawTunerBarIndexed(4, 6, 7, bwDP, bwW, clrDark, 0.25, true, true); glContext->DrawTunerBarIndexed(7, 7, 7, bwDP, bwW, clrMid, 0.25, true, true); glContext->DrawTunerBarIndexed(1, 3, 11, centerDP, centerW, clrMid, 0.25, true, true); // freq glContext->DrawTunerBarIndexed(4, 6, 11, centerDP, centerW, clrDark, 0.25, true, true); glContext->DrawTunerBarIndexed(7, 9, 11, centerDP, centerW, clrMid, 0.25, true, true); glContext->DrawTunerBarIndexed(10, 11, 11, centerDP, centerW, clrDark, 0.25, true, true); if (hoverIndex > 0 && !mouseTracker.mouseDown()) { switch (hoverState) { case TUNING_HOVER_FREQ: glContext->DrawTunerBarIndexed(hoverIndex, hoverIndex, 11, freqDP, freqW, clr, 0.25, top, bottom); // freq break; case TUNING_HOVER_BW: glContext->DrawTunerBarIndexed(hoverIndex, hoverIndex, 7, bwDP, bwW, clr, 0.25, top, bottom); // bw break; case TUNING_HOVER_CENTER: glContext->DrawTunerBarIndexed(hoverIndex, hoverIndex, 11, centerDP, centerW, clr, 0.25, top, bottom); // center break; case TUNING_HOVER_NONE: break; case TUNING_HOVER_PPM: glContext->DrawTunerBarIndexed(hoverIndex, hoverIndex, 11, freqDP, freqW, clr, 0.25, top, bottom); // freq break; } } if (altDown) { glContext->DrawTuner(currentPPM, 11, freqDP, freqW); } else { glContext->DrawTuner(freq, 11, freqDP, freqW); int snap = wxGetApp().getFrequencySnap(); if (snap != 1) { glContext->DrawTunerDigitBox((int)log10(snap), 11, freqDP, freqW, RGBA4f(1.0,0.0,0.0)); } } glContext->DrawTuner(halfBand?(bw/2):bw, 7, bwDP, bwW); glContext->DrawTuner(center, 11, centerDP, centerW); glContext->DrawEnd(); SwapBuffers(); } void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) { double exp = pow(10, exponent); long long amount = up?exp:-exp; if (halfBand && state == TUNING_HOVER_BW) { amount *= 2; } auto activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (state == TUNING_HOVER_FREQ && activeDemod) { long long freq = activeDemod->getFrequency(); long long diff = abs(wxGetApp().getFrequency() - freq); if (shiftDown) { bool carried = (long long)((freq) / (exp * 10)) != (long long)((freq + amount) / (exp * 10)) || (bottom && freq < exp); freq += carried?(9*-amount):amount; } else { freq += amount; } if (wxGetApp().getSampleRate() / 2 < diff) { wxGetApp().setFrequency(freq); } activeDemod->setTracking(true); activeDemod->setFollow(true); activeDemod->setFrequency(freq); if (activeDemod->isDeltaLock()) { activeDemod->setDeltaLockOfs(activeDemod->getFrequency() - wxGetApp().getFrequency()); } activeDemod->updateLabel(freq); } if (state == TUNING_HOVER_BW) { long bw = wxGetApp().getDemodMgr().getLastBandwidth(); if (shiftDown) { bool carried = (long)((bw) / (exp * 10)) != (long)((bw + amount) / (exp * 10)) || (bottom && bw < exp); bw += carried?(9*-amount):amount; } else { bw += amount; } if (bw > CHANNELIZER_RATE_MAX) { bw = CHANNELIZER_RATE_MAX; } wxGetApp().getDemodMgr().setLastBandwidth(bw); if (activeDemod) { activeDemod->setBandwidth(wxGetApp().getDemodMgr().getLastBandwidth()); } } if (state == TUNING_HOVER_CENTER) { long long ctr = wxGetApp().getFrequency(); if (shiftDown) { bool carried = (long long)((ctr) / (exp * 10)) != (long long)((ctr + amount) / (exp * 10)) || (bottom && ctr < exp); ctr += carried?(9*-amount):amount; } else { ctr += amount; } wxGetApp().setFrequency(ctr); } if (state == TUNING_HOVER_PPM) { if (shiftDown) { bool carried = (long long)((currentPPM) / (exp * 10)) != (long long)((currentPPM + amount) / (exp * 10)) || (bottom && currentPPM < exp); currentPPM += carried?(9*-amount):amount; } else { currentPPM += amount; } if (currentPPM > 2000) { currentPPM = 2000; } if (currentPPM < -2000) { currentPPM = -2000; } wxGetApp().setPPM(currentPPM); wxGetApp().notifyMainUIOfDeviceChange(); } } void TuningCanvas::OnIdle(wxIdleEvent &event) { if (mouseTracker.mouseDown()) { if (downState != TUNING_HOVER_NONE) { dragAccum += 5.0*mouseTracker.getOriginDeltaMouseX(); while (dragAccum > 1.0) { StepTuner(downState, downIndex-1, true); dragAccum -= 1.0; dragging = true; } while (dragAccum < -1.0) { StepTuner(downState, downIndex-1, false); dragAccum += 1.0; dragging = true; } } else { dragAccum = 0; dragging = false; } } if (mouseTracker.mouseInView() || changed()) { Refresh(); } event.RequestMore(); } void TuningCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); int index = 0; top = mouseTracker.getMouseY() >= 0.5; bottom = mouseTracker.getMouseY() <= 0.5; index = glContext->GetTunerDigitIndex(mouseTracker.getMouseX(), 11, freqDP, freqW); // freq if (index > 0) { hoverIndex = index; hoverState = altDown?TUNING_HOVER_PPM:TUNING_HOVER_FREQ; } if (!index) { index = glContext->GetTunerDigitIndex(mouseTracker.getMouseX(), 7, bwDP, bwW); // bw if (index > 0) { hoverIndex = index; hoverState = TUNING_HOVER_BW; } } if (!index) { index = glContext->GetTunerDigitIndex(mouseTracker.getMouseX(), 11, centerDP, centerW); // center if (index > 0) { hoverIndex = index; hoverState = TUNING_HOVER_CENTER; } } if (!index) { hoverIndex = 0; hoverState = TUNING_HOVER_NONE; } else { switch (hoverState) { case TUNING_HOVER_FREQ: setStatusText("Click, wheel or drag a digit to change frequency; SPACE or numeric key for direct input. Right click to set/clear snap. Hold ALT to change PPM. Hold SHIFT to disable carry."); break; case TUNING_HOVER_BW: setStatusText("Click, wheel or drag a digit to change bandwidth; SPACE or numeric key for direct input. Hold SHIFT to disable carry."); break; case TUNING_HOVER_CENTER: setStatusText("Click, wheel or drag a digit to change center frequency; SPACE or numeric key for direct input. Hold SHIFT to disable carry."); break; case TUNING_HOVER_PPM: setStatusText("Click, wheel or drag a digit to change device PPM offset. Hold SHIFT to disable carry."); break; case TUNING_HOVER_NONE: setStatusText(""); break; } } if (hoverState == TUNING_HOVER_BW || hoverState == TUNING_HOVER_FREQ) { wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator()); } else { wxGetApp().getDemodMgr().setActiveDemodulator(nullptr); } } void TuningCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); uxDown = 2.0 * (mouseTracker.getMouseX() - 0.5); dragAccum = 0; mouseTracker.setVertDragLock(true); downIndex = hoverIndex; downState = hoverState; } void TuningCanvas::OnMouseWheelMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseWheelMoved(event); int hExponent = hoverIndex - 1; if (hoverState != TUNING_HOVER_NONE && !mouseTracker.mouseDown() && hoverIndex) { if (event.m_wheelAxis == wxMOUSE_WHEEL_VERTICAL) { StepTuner(hoverState, hExponent, (event.m_wheelRotation > 0)?true:false); } else { StepTuner(hoverState, hExponent, (event.m_wheelRotation < 0)?true:false); } } } void TuningCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); int hExponent = hoverIndex - 1; if (hoverState != TUNING_HOVER_NONE && !dragging && (downState == hoverState) && (downIndex == hoverIndex)) { StepTuner(hoverState, hExponent, top); } mouseTracker.setVertDragLock(false); dragging = false; SetCursor(wxCURSOR_ARROW); } void TuningCanvas::OnMouseRightDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseRightDown(event); } void TuningCanvas::OnMouseRightReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseRightReleased(event); if (hoverState == TUNING_HOVER_FREQ) { if (hoverIndex == 1) { wxGetApp().setFrequencySnap(1); } else if (hoverIndex > 1 && hoverIndex < 8) { int exp = pow(10, hoverIndex-1); if (wxGetApp().getFrequencySnap() == exp) { wxGetApp().setFrequencySnap(1); } else { wxGetApp().setFrequencySnap(exp); } } } } void TuningCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor(wxCURSOR_CROSS); hoverIndex = 0; hoverState = TUNING_HOVER_NONE; wxGetApp().getDemodMgr().setActiveDemodulator(nullptr); if (currentPPM != lastPPM) { wxGetApp().saveConfig(); } Refresh(); } void TuningCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); SetCursor(wxCURSOR_ARROW); hoverIndex = 0; hoverState = TUNING_HOVER_NONE; lastPPM = currentPPM = wxGetApp().getPPM(); #ifdef _WIN32 if (wxGetApp().getAppFrame()->canFocus()) { this->SetFocus(); } #endif } void TuningCanvas::setHelpTip(std::string tip) { helpTip = tip; } void TuningCanvas::OnKeyDown(wxKeyEvent& event) { InteractiveCanvas::OnKeyDown(event); if (event.GetKeyCode() == WXK_SPACE) { if (hoverState == TUNING_HOVER_CENTER || hoverState == TUNING_HOVER_FREQ) { wxGetApp().showFrequencyInput(FrequencyDialog::FDIALOG_TARGET_DEFAULT); } else if (hoverState == TUNING_HOVER_BW) { wxGetApp().showFrequencyInput(FrequencyDialog::FDIALOG_TARGET_BANDWIDTH); } } } void TuningCanvas::OnKeyUp(wxKeyEvent& event) { InteractiveCanvas::OnKeyUp(event); } TuningCanvas::ActiveState TuningCanvas::getHoverState() { return hoverState; } CubicSDR-0.2.3/src/visual/TuningCanvas.h000066400000000000000000000033541322677621400177740ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include #include #include "InteractiveCanvas.h" #include "TuningContext.h" #include "MouseTracker.h" #include "Timer.h" class TuningCanvas: public InteractiveCanvas { public: enum ActiveState { TUNING_HOVER_NONE, TUNING_HOVER_FREQ, TUNING_HOVER_BW, TUNING_HOVER_PPM, TUNING_HOVER_CENTER }; TuningCanvas(wxWindow *parent, std::vector dispAttrs); ~TuningCanvas(); void setHelpTip(std::string tip); bool changed(); void setHalfBand(bool hb); void OnKeyDown(wxKeyEvent& event); void OnKeyUp(wxKeyEvent& event); ActiveState getHoverState(); private: void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); void OnMouseMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); void OnMouseRightDown(wxMouseEvent& event); void OnMouseRightReleased(wxMouseEvent& event); void StepTuner(ActiveState state, int factor, bool up = true); TuningContext *glContext; std::string helpTip; float dragAccum; float uxDown; ActiveState hoverState; ActiveState downState; int hoverIndex; int downIndex; bool dragging; float freqDP; float freqW; float bwDP; float bwW; float centerDP; float centerW; bool top; bool bottom; int currentPPM; int lastPPM; long long freq, bw, center; bool halfBand; // wxDECLARE_EVENT_TABLE(); }; CubicSDR-0.2.3/src/visual/TuningContext.cpp000066400000000000000000000133071322677621400205370ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "TuningContext.h" #include "TuningCanvas.h" #include "ColorTheme.h" // http://stackoverflow.com/questions/7276826/c-format-number-with-commas class comma_numpunct: public std::numpunct { protected: virtual char do_thousands_sep() const { return ','; } virtual std::string do_grouping() const { return "\03"; } }; TuningContext::TuningContext(TuningCanvas *canvas, wxGLContext *sharedContext) : PrimaryGLContext(canvas, sharedContext) { glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); comma_locale = std::locale(std::locale(), new comma_numpunct()); freqStrFormatted.imbue(comma_locale); } void TuningContext::DrawBegin() { glClearColor(ThemeMgr::mgr.currentTheme->generalBackground.r, ThemeMgr::mgr.currentTheme->generalBackground.g, ThemeMgr::mgr.currentTheme->generalBackground.b, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_TEXTURE_2D); } void TuningContext::Draw(float r, float g, float b, float a, float p1, float p2) { glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); glBegin(GL_QUADS); glColor4f(r * 0.5, g * 0.5, b * 0.5, a); glVertex2f(-1.0 + p2 * 2.0, 1.0); glVertex2f(-1.0 + p1 * 2.0, 1.0); glColor4f(r, g, b, a); glVertex2f(-1.0 + p1 * 2.0, 0.0); glVertex2f(-1.0 + p2 * 2.0, 0.0); glVertex2f(-1.0 + p2 * 2.0, 0.0); glVertex2f(-1.0 + p1 * 2.0, 0.0); glColor4f(r * 0.5, g * 0.5, b * 0.5, a); glVertex2f(-1.0 + p1 * 2.0, -1.0); glVertex2f(-1.0 + p2 * 2.0, -1.0); glEnd(); glDisable(GL_BLEND); } void TuningContext::DrawEnd() { // glFlush(); // CheckGLError(); } void TuningContext::DrawTuner(long long freq, int count, float displayPos, float displayWidth) { GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); float viewWidth = (float) vp[2]; float viewHeight = (float) vp[3]; freqStr.str(""); freqStr << freq; std::string freqChars = freqStr.str(); int fontSize = 32; if (viewWidth < 300) { fontSize = 18; } else if (viewWidth < 500) { fontSize = 24; } if (viewHeight < 18) { fontSize = 12; } else if (viewHeight < 24) { fontSize = 16; } else if (viewHeight < 28) { fontSize = 18; } glColor3f(ThemeMgr::mgr.currentTheme->text.r, ThemeMgr::mgr.currentTheme->text.g, ThemeMgr::mgr.currentTheme->text.b); int numChars = freqChars.length(); int ofs = count - numChars; //do not zoom this one: GLFont::Drawer refDrawingFont = GLFont::getFont(fontSize); for (int i = ofs; i < count; i++) { float xpos = displayPos + (displayWidth / (float) count) * (float) i + ((displayWidth / 2.0) / (float) count); refDrawingFont.drawString(freqStr.str().substr(i - ofs, 1), xpos, 0, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); } glColor4f(0.65f, 0.65f, 0.65f, 0.25f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBegin(GL_LINES); for (int i = count; i >= 0; i--) { float xpos = displayPos + (displayWidth / (float) count) * (float) i; glVertex2f(xpos, -1.0); glVertex2f(xpos, 1.0); } glEnd(); glDisable(GL_BLEND); } void TuningContext::DrawTunerDigitBox(int index, int count, float displayPos, float displayWidth, RGBA4f /* c */) { GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float pixelHeight = 2.0/viewHeight; glColor4f(1.0, 0,0,1); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); float xpos = displayPos + (displayWidth / (float) count) * (float) (count-index); float xpos2 = displayPos + (displayWidth / (float) count) * (float) ((count-1)-index); glBegin(GL_LINE_STRIP); glVertex2f(xpos, 1.0-pixelHeight); glVertex2f(xpos, -1.0+pixelHeight); glVertex2f(xpos2, -1.0+pixelHeight); glVertex2f(xpos2, 1.0-pixelHeight); glVertex2f(xpos, 1.0-pixelHeight); glEnd(); glDisable(GL_BLEND); } int TuningContext::GetTunerDigitIndex(float mPos, int count, float displayPos, float displayWidth) { mPos -= 0.5; mPos *= 2.0; float delta = mPos - displayPos; if (delta < 0 || delta > displayWidth) { return 0; } int index = floor((delta / displayWidth) * (count)); return count - index; } void TuningContext::DrawTunerBarIndexed(int start, int end, int count, float displayPos, float displayWidth, RGBA4f color, float /* alpha */, bool top, bool bottom) { float ofs = (displayWidth / (float) count); float p2 = displayPos + ofs * (float) (count - start + 1); float p1 = displayPos + ofs * (float) (count - end); float r = color.r, g = color.g, b = color.b, a = 0.6f; glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); glBegin(GL_QUADS); if (top) { glColor4f(r * 0.5, g * 0.5, b * 0.5, a); glVertex2f(p2, 1.0); glVertex2f(p1, 1.0); glColor4f(r, g, b, a); glVertex2f(p1, 0.0); glVertex2f(p2, 0.0); } if (bottom) { glColor4f(r, g, b, a); glVertex2f(p2, 0.0); glVertex2f(p1, 0.0); glColor4f(r * 0.5, g * 0.5, b * 0.5, a); glVertex2f(p1, -1.0); glVertex2f(p2, -1.0); } glEnd(); glDisable(GL_BLEND); } void TuningContext::DrawDemodFreqBw(long long freq, unsigned int bw, long long center) { DrawTuner(freq, 11, -1.0, (1.0f / 3.0f) * 2.0f); DrawTuner(bw, 7, -1.0 + (2.25f / 3.0f), (1.0f / 4.0f) * 2.0f); DrawTuner(center, 11, -1.0 + (2.0f / 3.0f) * 2.0, (1.0f / 3.0f) * 2.0f); } CubicSDR-0.2.3/src/visual/TuningContext.h000066400000000000000000000020251322677621400201770ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "PrimaryGLContext.h" #include "Gradient.h" #define NUM_WATERFALL_LINES 512 class TuningCanvas; class TuningContext: public PrimaryGLContext { public: TuningContext(TuningCanvas *canvas, wxGLContext *sharedContext); void DrawBegin(); void Draw(float r, float g, float b, float a, float p1, float p2); void DrawTuner(long long freq, int count, float displayPos, float displayWidth); void DrawTunerDigitBox(int index, int count, float displayPos, float displayWidth, RGBA4f c); int GetTunerDigitIndex(float mPos, int count, float displayPos, float displayWidth); void DrawTunerBarIndexed(int start, int end, int count, float displayPos, float displayWidth, RGBA4f color, float alpha, bool top, bool bottom); void DrawDemodFreqBw(long long freq, unsigned int bw, long long center); void DrawEnd(); private: std::locale comma_locale; std::stringstream freqStr; std::stringstream freqStrFormatted; }; CubicSDR-0.2.3/src/visual/WaterfallCanvas.cpp000066400000000000000000000761441322677621400210130ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #include "WaterfallCanvas.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include "CubicSDRDefs.h" #include "AppFrame.h" #include #ifdef USE_HAMLIB #include "RigThread.h" #endif #include #include "DemodulatorThread.h" wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint) EVT_IDLE(WaterfallCanvas::OnIdle) EVT_MOTION(WaterfallCanvas::OnMouseMoved) EVT_LEFT_DOWN(WaterfallCanvas::OnMouseDown) EVT_LEFT_UP(WaterfallCanvas::OnMouseReleased) EVT_RIGHT_DOWN(WaterfallCanvas::OnMouseRightDown) EVT_RIGHT_UP(WaterfallCanvas::OnMouseRightReleased) EVT_LEAVE_WINDOW(WaterfallCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(WaterfallCanvas::OnMouseEnterWindow) EVT_MOUSEWHEEL(WaterfallCanvas::OnMouseWheelMoved) wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, std::vector dispAttrs) : InteractiveCanvas(parent, dispAttrs), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), new_fft_size(0), waterfall_lines(0), dragOfs(0), mouseZoom(1), zoom(1), freqMoving(false), freqMove(0.0), hoverAlpha(1.0) { glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); linesPerSecond = DEFAULT_WATERFALL_LPS; lpsIndex = 0; preBuf = false; SetCursor(wxCURSOR_CROSS); scaleMove = 0; minBandwidth = 30000; fft_size_changed.store(false); } WaterfallCanvas::~WaterfallCanvas() { } void WaterfallCanvas::setup(unsigned int fft_size_in, int waterfall_lines_in) { if (fft_size == fft_size_in && waterfall_lines_in == waterfall_lines) { return; } fft_size = fft_size_in; waterfall_lines = waterfall_lines_in; waterfallPanel.setup(fft_size, waterfall_lines); gTimer.start(); } void WaterfallCanvas::setFFTSize(unsigned int fft_size_in) { if (fft_size_in == fft_size) { return; } new_fft_size = fft_size_in; fft_size_changed.store(true); } WaterfallCanvas::DragState WaterfallCanvas::getDragState() { return dragState; } WaterfallCanvas::DragState WaterfallCanvas::getNextDragState() { return nextDragState; } void WaterfallCanvas::attachSpectrumCanvas(SpectrumCanvas *canvas_in) { spectrumCanvas = canvas_in; } void WaterfallCanvas::processInputQueue() { std::lock_guard < std::mutex > lock(tex_update); gTimer.update(); double targetVis = 1.0 / (double)linesPerSecond; lpsIndex += gTimer.lastUpdateSeconds(); bool updated = false; if (linesPerSecond) { if (lpsIndex >= targetVis) { while (lpsIndex >= targetVis) { SpectrumVisualDataPtr vData; if (visualDataQueue->try_pop(vData)) { if (vData) { if (vData->spectrum_points.size() == fft_size * 2) { waterfallPanel.setPoints(vData->spectrum_points); } waterfallPanel.step(); updated = true; } lpsIndex-=targetVis; } else { break; } } } } if (updated) { wxClientDC(this); glContext->SetCurrent(*this); waterfallPanel.update(); } } void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { std::lock_guard < std::mutex > lock(tex_update); wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); long double currentZoom = zoom; if (mouseZoom != 1) { currentZoom = mouseZoom; mouseZoom = mouseZoom + (1.0 - mouseZoom) * 0.2; if (fabs(mouseZoom-1.0)<0.01) { mouseZoom = 1; } } if (scaleMove != 0) { SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor(); FFTVisualDataThread *wdt = wxGetApp().getAppFrame()->getWaterfallDataThread(); SpectrumVisualProcessor *wp = wdt->getProcessor(); float factor = sp->getScaleFactor(); factor += scaleMove * 0.02; if (factor < 0.25) { factor = 0.25; } if (factor > 10.0) { factor = 10.0; } sp->setScaleFactor(factor); wp->setScaleFactor(factor); } if (freqMove != 0.0) { long long newFreq = getCenterFrequency() + (long long)((long double)getBandwidth()*freqMove) * 0.01; long long minFreq = bandwidth/2; if (newFreq < minFreq) { newFreq = minFreq; } updateCenterFrequency(newFreq); if (!freqMoving) { freqMove -= (freqMove * 0.2); if (fabs(freqMove) < 0.01) { freqMove = 0.0; } } } long long bw; if (currentZoom != 1) { long long freq = wxGetApp().getFrequency(); bw = getBandwidth(); double mpos = 0; float mouseInView = false; if (mouseTracker.mouseInView()) { mpos = mouseTracker.getMouseX(); mouseInView = true; } else if (spectrumCanvas && spectrumCanvas->getMouseTracker()->mouseInView()) { mpos = spectrumCanvas->getMouseTracker()->getMouseX(); mouseInView = true; } if (currentZoom < 1) { bw = (long long) ceil((long double) bw * currentZoom); if (bw < minBandwidth) { bw = minBandwidth; } if (mouseInView) { long long mfreqA = getFrequencyAt(mpos, centerFreq, getBandwidth()); long long mfreqB = getFrequencyAt(mpos, centerFreq, bw); centerFreq += mfreqA - mfreqB; } setView(centerFreq, bw); } else { if (isView) { bw = (long long) ceil((long double) bw * currentZoom); if (bw >= wxGetApp().getSampleRate()) { disableView(); if (spectrumCanvas) { spectrumCanvas->disableView(); } bw = wxGetApp().getSampleRate(); centerFreq = wxGetApp().getFrequency(); } else { if (mouseInView) { long long mfreqA = getFrequencyAt(mpos, centerFreq, getBandwidth()); long long mfreqB = getFrequencyAt(mpos, centerFreq, bw); centerFreq += mfreqA - mfreqB; setBandwidth(bw); } else { setBandwidth(bw); } } } } if (centerFreq < freq && (centerFreq - bandwidth / 2) < (freq - wxGetApp().getSampleRate() / 2)) { centerFreq = (freq - wxGetApp().getSampleRate() / 2) + bandwidth / 2; } if (centerFreq > freq && (centerFreq + bandwidth / 2) > (freq + wxGetApp().getSampleRate() / 2)) { centerFreq = (freq + wxGetApp().getSampleRate() / 2) - bandwidth / 2; } if (spectrumCanvas) { if ((spectrumCanvas->getCenterFrequency() != centerFreq) || (spectrumCanvas->getBandwidth() != bw)) { if (getViewState()) { spectrumCanvas->setView(centerFreq,bw); } else { spectrumCanvas->disableView(); spectrumCanvas->setCenterFrequency(centerFreq); spectrumCanvas->setBandwidth(bw); } } } } glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); if (fft_size_changed.load()) { fft_size = new_fft_size; waterfallPanel.setup(fft_size, waterfall_lines); fft_size_changed.store(false); } glContext->BeginDraw(0,0,0); waterfallPanel.calcTransform(CubicVR::mat4::identity()); waterfallPanel.draw(); auto demods = wxGetApp().getDemodMgr().getDemodulators(); auto activeDemodulator = wxGetApp().getDemodMgr().getActiveDemodulator(); auto lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator(); bool isNew = shiftDown || (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive()); int currentBandwidth = getBandwidth(); long long currentCenterFreq = getCenterFrequency(); ColorTheme *currentTheme = ThemeMgr::mgr.currentTheme; std::string last_type = wxGetApp().getDemodMgr().getLastDemodulatorType(); if (mouseTracker.mouseInView() || wxGetApp().getDemodMgr().getActiveDemodulator()) { hoverAlpha += (1.0f-hoverAlpha)*0.1f; if (hoverAlpha > 1.5f) { hoverAlpha = 1.5f; } glContext->setHoverAlpha(hoverAlpha); if (nextDragState == WF_DRAG_RANGE) { float width = (1.0 / (float) ClientSize.x); float rangeWidth = mouseTracker.getOriginDeltaMouseX(); float centerPos; if (mouseTracker.mouseDown()) { if (rangeWidth) { width = rangeWidth; } centerPos = mouseTracker.getOriginMouseX() + width / 2.0; } else { centerPos = mouseTracker.getMouseX(); } glContext->DrawDemod(lastActiveDemodulator, isNew?currentTheme->waterfallHighlight:currentTheme->waterfallDestroy, currentCenterFreq, currentBandwidth); if ((last_type == "LSB" || last_type == "USB") && mouseTracker.mouseDown()) { centerPos = mouseTracker.getMouseX(); glContext->DrawRangeSelector(centerPos, centerPos-width, isNew?currentTheme->waterfallNew:currentTheme->waterfallHover); } else { glContext->DrawFreqSelector(centerPos, isNew?currentTheme->waterfallNew:currentTheme->waterfallHover, width, currentCenterFreq, currentBandwidth); } } else { if (lastActiveDemodulator) { glContext->DrawDemod(lastActiveDemodulator, ((isNew && activeDemodulator == nullptr) || (activeDemodulator != nullptr))?currentTheme->waterfallHighlight:currentTheme->waterfallDestroy, currentCenterFreq, currentBandwidth); } if (activeDemodulator == nullptr) { glContext->DrawFreqSelector(mouseTracker.getMouseX(), ((isNew && lastActiveDemodulator) || (!lastActiveDemodulator) )?currentTheme->waterfallNew:currentTheme->waterfallHover, 0, currentCenterFreq, currentBandwidth); } else { glContext->DrawDemod(activeDemodulator, currentTheme->waterfallHover, currentCenterFreq, currentBandwidth); } } } else { hoverAlpha += (0.0f-hoverAlpha)*0.05f; if (hoverAlpha < 1.0e-5f) { hoverAlpha = 0; } glContext->setHoverAlpha(hoverAlpha); if (activeDemodulator) { glContext->DrawDemod(activeDemodulator, currentTheme->waterfallHighlight, currentCenterFreq, currentBandwidth); } if (lastActiveDemodulator) { glContext->DrawDemod(lastActiveDemodulator, currentTheme->waterfallHighlight, currentCenterFreq, currentBandwidth); } } glContext->setHoverAlpha(0); for (int i = 0, iMax = demods.size(); i < iMax; i++) { if (!demods[i]->isActive()) { continue; } if (activeDemodulator == demods[i] || lastActiveDemodulator == demods[i]) { continue; } glContext->DrawDemod(demods[i], currentTheme->waterfallHighlight, currentCenterFreq, currentBandwidth); } for (int i = 0, iMax = demods.size(); i < iMax; i++) { demods[i]->getVisualCue()->step(); int squelchBreak = demods[i]->getVisualCue()->getSquelchBreak(); if (squelchBreak) { glContext->setHoverAlpha((float(squelchBreak) / 60.0)); glContext->DrawDemod(demods[i], currentTheme->waterfallHover, currentCenterFreq, currentBandwidth); } } glContext->EndDraw(); SwapBuffers(); } void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) { InteractiveCanvas::OnKeyUp(event); shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); switch (event.GetKeyCode()) { case WXK_UP: case WXK_NUMPAD_UP: scaleMove = 0.0; zoom = 1.0; if (mouseZoom != 1.0) { mouseZoom = 0.95f; } break; case WXK_DOWN: case WXK_NUMPAD_DOWN: scaleMove = 0.0; zoom = 1.0; if (mouseZoom != 1.0) { mouseZoom = 1.05f; } break; case WXK_LEFT: case WXK_NUMPAD_LEFT: case WXK_RIGHT: case WXK_NUMPAD_RIGHT: freqMoving = false; break; } } void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { InteractiveCanvas::OnKeyDown(event); auto activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); long long originalFreq = getCenterFrequency(); long long freq = originalFreq; switch (event.GetKeyCode()) { case WXK_UP: case WXK_NUMPAD_UP: if (!shiftDown) { mouseZoom = 1.0; zoom = 0.95f; } else { scaleMove = 1.0; } break; case WXK_DOWN: case WXK_NUMPAD_DOWN: if (!shiftDown) { mouseZoom = 1.0; zoom = 1.05f; } else { scaleMove = -1.0; } break; case WXK_RIGHT: case WXK_NUMPAD_RIGHT: if (isView) { freqMove = shiftDown?5.0:1.0; freqMoving = true; } else { freq += shiftDown?(getBandwidth() * 10):(getBandwidth() / 2); } break; case WXK_LEFT: case WXK_NUMPAD_LEFT: if (isView) { freqMove = shiftDown?-5.0:-1.0; freqMoving = true; } else { freq -= shiftDown?(getBandwidth() * 10):(getBandwidth() / 2); } break; case 'D': case WXK_DELETE: if (!activeDemod) { break; } wxGetApp().removeDemodulator(activeDemod); wxGetApp().getDemodMgr().deleteThread(activeDemod); break; case WXK_SPACE: wxGetApp().showFrequencyInput(); break; case 'E': //E is for 'Edit the label' of the active demodulator. wxGetApp().showLabelInput(); break; case 'C': if (wxGetApp().getDemodMgr().getActiveDemodulator()) { wxGetApp().setFrequency(wxGetApp().getDemodMgr().getActiveDemodulator()->getFrequency()); } else if (mouseTracker.mouseInView()) { long long freq = getFrequencyAt(mouseTracker.getMouseX()); int snap = wxGetApp().getFrequencySnap(); if (snap > 1) { freq = roundf((float)freq/(float)snap)*snap; } wxGetApp().setFrequency(freq); } #ifdef USE_HAMLIB if (wxGetApp().rigIsActive() && (!wxGetApp().getRigThread()->getControlMode() || wxGetApp().getRigThread()->getCenterLock())) { wxGetApp().getRigThread()->setFrequency(wxGetApp().getFrequency(),true); } #endif break; default: event.Skip(); return; } long long minFreq = bandwidth/2; if (freq < minFreq) { freq = minFreq; } if (freq != originalFreq) { updateCenterFrequency(freq); } } void WaterfallCanvas::OnIdle(wxIdleEvent &event) { processInputQueue(); Refresh(); event.RequestMore(); } void WaterfallCanvas::updateHoverState() { long long freqPos = getFrequencyAt(mouseTracker.getMouseX()); auto demodsHover = wxGetApp().getDemodMgr().getDemodulatorsAt(freqPos, 15000); wxGetApp().getDemodMgr().setActiveDemodulator(nullptr); if (altDown) { nextDragState = WF_DRAG_RANGE; mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); if (shiftDown) { setStatusText("Click and drag to create a new demodulator by range."); } else { setStatusText("Click and drag to set the current demodulator range."); } } else if (demodsHover.size() && !shiftDown) { long near_dist = getBandwidth(); DemodulatorInstancePtr activeDemodulator = nullptr; for (int i = 0, iMax = demodsHover.size(); i < iMax; i++) { auto demod = demodsHover[i]; long long freqDiff = demod->getFrequency() - freqPos; long halfBw = (demod->getBandwidth() / 2); long long currentBw = getBandwidth(); long long globalBw = wxGetApp().getSampleRate(); long dist = abs(freqDiff); double bufferBw = 10000.0 * ((double)currentBw / (double)globalBw); double maxDist = ((double)halfBw + bufferBw); if ((double)dist <= maxDist) { if ((freqDiff > 0 && demod->getDemodulatorType() == "USB") || (freqDiff < 0 && demod->getDemodulatorType() == "LSB")) { continue; } if (dist < near_dist) { activeDemodulator = demod; near_dist = dist; } long edge_dist = abs(halfBw - dist); if (edge_dist < near_dist) { activeDemodulator = demod; near_dist = edge_dist; } } } if (activeDemodulator == nullptr) { nextDragState = WF_DRAG_NONE; SetCursor(wxCURSOR_CROSS); return; } wxGetApp().getDemodMgr().setActiveDemodulator(activeDemodulator); long long freqDiff = activeDemodulator->getFrequency() - freqPos; if (abs(freqDiff) > (activeDemodulator->getBandwidth() / 3)) { if (freqDiff > 0) { if (activeDemodulator->getDemodulatorType() != "USB") { nextDragState = WF_DRAG_BANDWIDTH_LEFT; SetCursor(wxCURSOR_SIZEWE); } } else { if (activeDemodulator->getDemodulatorType() != "LSB") { nextDragState = WF_DRAG_BANDWIDTH_RIGHT; SetCursor(wxCURSOR_SIZEWE); } } mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); setStatusText("Drag to change bandwidth. SPACE or 0-9 for direct frequency input. [, ] to nudge, M for mute, D to delete, C to center, E to edit label, R to record."); } else { SetCursor(wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); setStatusText("Drag to change frequency; SPACE or 0-9 for direct input. [, ] to nudge, M for mute, D to delete, C to center, E to edit label, R to record."); } } else { SetCursor(wxCURSOR_CROSS); nextDragState = WF_DRAG_NONE; if (shiftDown) { setStatusText("Click to create a new demodulator or hold ALT to drag new range."); } else { setStatusText( "Click to set demodulator frequency or hold ALT to drag range; hold SHIFT to create new. Right drag or wheel to Zoom. Arrow keys to navigate/zoom, C to center. Shift-R record/stop all."); } } } void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); auto demod = wxGetApp().getDemodMgr().getActiveDemodulator(); if (mouseTracker.mouseDown()) { if (demod == nullptr) { return; } if (dragState == WF_DRAG_BANDWIDTH_LEFT || dragState == WF_DRAG_BANDWIDTH_RIGHT) { int bwDiff = (int) (mouseTracker.getDeltaMouseX() * (float) getBandwidth()) * 2; if (dragState == WF_DRAG_BANDWIDTH_LEFT) { bwDiff = -bwDiff; } int currentBW = dragBW; currentBW = currentBW + bwDiff; if (currentBW > CHANNELIZER_RATE_MAX) { currentBW = CHANNELIZER_RATE_MAX; } if (currentBW < MIN_BANDWIDTH) { currentBW = MIN_BANDWIDTH; } demod->setBandwidth(currentBW); dragBW = currentBW; } if (dragState == WF_DRAG_FREQUENCY) { long long bwTarget = getFrequencyAt(mouseTracker.getMouseX()) - dragOfs; long long currentFreq = demod->getFrequency(); long long bwDiff = bwTarget - currentFreq; int snap = wxGetApp().getFrequencySnap(); if (snap > 1) { bwDiff = roundf((float)bwDiff/(float)snap)*snap; } if (bwDiff) { demod->setFrequency(currentFreq + bwDiff); if (demod->isDeltaLock()) { demod->setDeltaLockOfs(demod->getFrequency() - wxGetApp().getFrequency()); } currentFreq = demod->getFrequency(); demod->updateLabel(currentFreq); } } } else if (mouseTracker.mouseRightDown()) { mouseZoom = mouseZoom + ((1.0 - (mouseTracker.getDeltaMouseY() * 4.0)) - mouseZoom) * 0.1; } else { updateHoverState(); } } void WaterfallCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); updateHoverState(); dragState = nextDragState; wxGetApp().getDemodMgr().updateLastState(); if (dragState && dragState != WF_DRAG_RANGE) { auto demod = wxGetApp().getDemodMgr().getActiveDemodulator(); if (demod) { dragOfs = (long long) (mouseTracker.getMouseX() * (float) getBandwidth()) + getCenterFrequency() - (getBandwidth() / 2) - demod->getFrequency(); dragBW = demod->getBandwidth(); } wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false); } } void WaterfallCanvas::OnMouseWheelMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseWheelMoved(event); float movement = (float)event.GetWheelRotation() / (float)event.GetLinesPerAction(); mouseZoom = 1.0f - movement/1000.0f; } void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); wxGetApp().getDemodMgr().updateLastState(); bool isNew = shiftDown || (wxGetApp().getDemodMgr().getLastActiveDemodulator() == nullptr) || (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive()); mouseTracker.setVertDragLock(false); mouseTracker.setHorizDragLock(false); DemodulatorInstancePtr demod = isNew?nullptr:wxGetApp().getDemodMgr().getLastActiveDemodulator(); DemodulatorInstancePtr activeDemod = isNew?nullptr:wxGetApp().getDemodMgr().getActiveDemodulator(); DemodulatorMgr *mgr = &wxGetApp().getDemodMgr(); if (mouseTracker.getOriginDeltaMouseX() == 0 && mouseTracker.getOriginDeltaMouseY() == 0) { float pos = mouseTracker.getMouseX(); long long input_center_freq = getCenterFrequency(); long long freqTarget = input_center_freq - (long long) (0.5 * (float) getBandwidth()) + (long long) ((float) pos * (float) getBandwidth()); long long demodFreq = demod?demod->getFrequency():freqTarget; long long bwDiff = freqTarget - demodFreq; long long freq = demodFreq; int snap = wxGetApp().getFrequencySnap(); if (snap > 1) { if (demod) { bwDiff = roundf((double)bwDiff/(double)snap)*snap; freq += bwDiff; } else { freq = roundl((long double)freq/(double)snap)*snap; } } else { freq += bwDiff; } if (dragState == WF_DRAG_NONE) { if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) { mgr->updateLastState(); demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); } else { isNew = true; demod = wxGetApp().getDemodMgr().newThread(); demod->setFrequency(freq); demod->setDemodulatorType(mgr->getLastDemodulatorType()); demod->setBandwidth(mgr->getLastBandwidth()); demod->setSquelchLevel(mgr->getLastSquelchLevel()); demod->setSquelchEnabled(mgr->isLastSquelchEnabled()); demod->setGain(mgr->getLastGain()); demod->setMuted(mgr->isLastMuted()); if (mgr->getLastDeltaLock()) { demod->setDeltaLock(true); demod->setDeltaLockOfs(wxGetApp().getFrequency()-freq); } else { demod->setDeltaLock(false); } demod->writeModemSettings(mgr->getLastModemSettings(mgr->getLastDemodulatorType())); demod->run(); wxGetApp().notifyDemodulatorsChanged(); DemodulatorThread::releaseSquelchLock(nullptr); } if (!demod) { dragState = WF_DRAG_NONE; return; } demod->updateLabel(freq); demod->setFrequency(freq); if (demod->isDeltaLock()) { demod->setDeltaLockOfs(demod->getFrequency() - wxGetApp().getFrequency()); } if (isNew) { setStatusText("New demodulator at frequency: %s", freq); } else { setStatusText("Moved demodulator to frequency: %s", freq); } wxGetApp().getDemodMgr().setActiveDemodulator(demod, false); SetCursor(wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); } else { if (activeDemod) { wxGetApp().getDemodMgr().setActiveDemodulator(activeDemod, false); mgr->updateLastState(); activeDemod->setTracking(true); nextDragState = WF_DRAG_FREQUENCY; } else { nextDragState = WF_DRAG_NONE; } } } else if (dragState == WF_DRAG_RANGE) { float width = mouseTracker.getOriginDeltaMouseX(); float pos; std::string last_type = mgr->getLastDemodulatorType(); if (last_type == "LSB" || last_type == "USB") { float pos1 = mouseTracker.getOriginMouseX(); float pos2 = mouseTracker.getMouseX(); if (pos2 < pos1) { float tmp = pos1; pos1 = pos2; pos2 = tmp; } pos = (last_type == "LSB")?pos2:pos1; width *= 2; } else { pos = mouseTracker.getOriginMouseX() + width / 2.0; } long long input_center_freq = getCenterFrequency(); long long freq = input_center_freq - (long long) (0.5 * (float) getBandwidth()) + (long long) ((float) pos * (float) getBandwidth()); unsigned int bw = (unsigned int) (fabs(width) * (float) getBandwidth()); if (bw < MIN_BANDWIDTH) { bw = MIN_BANDWIDTH; } if (!bw) { dragState = WF_DRAG_NONE; return; } int snap = wxGetApp().getFrequencySnap(); if (snap > 1) { freq = roundl((long double)freq/(double)snap)*snap; } if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) { mgr->updateLastState(); demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); } else { demod = wxGetApp().getDemodMgr().newThread(); demod->setFrequency(freq); demod->setDemodulatorType(mgr->getLastDemodulatorType()); demod->setBandwidth(bw); demod->setSquelchLevel(mgr->getLastSquelchLevel()); demod->setSquelchEnabled(mgr->isLastSquelchEnabled()); demod->setGain(mgr->getLastGain()); demod->setMuted(mgr->isLastMuted()); if (mgr->getLastDeltaLock()) { demod->setDeltaLock(true); demod->setDeltaLockOfs(wxGetApp().getFrequency()-freq); } else { demod->setDeltaLock(false); } demod->writeModemSettings(mgr->getLastModemSettings(mgr->getLastDemodulatorType())); demod->run(); wxGetApp().notifyDemodulatorsChanged(); } if (demod == nullptr) { dragState = WF_DRAG_NONE; return; } setStatusText("New demodulator at frequency: %s", freq); demod->updateLabel(freq); demod->setFrequency(freq); demod->setBandwidth(bw); mgr->setActiveDemodulator(demod, false); mgr->updateLastState(); } dragState = WF_DRAG_NONE; } void WaterfallCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor(wxCURSOR_CROSS); wxGetApp().getDemodMgr().setActiveDemodulator(nullptr); mouseZoom = 1.0; } void WaterfallCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseEnterWindow(event); SetCursor(wxCURSOR_CROSS); #ifdef _WIN32 if (wxGetApp().getAppFrame()->canFocus()) { this->SetFocus(); } #endif } void WaterfallCanvas::OnMouseRightDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseRightDown(event); SetCursor(wxCURSOR_SIZENS); mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(true); } void WaterfallCanvas::OnMouseRightReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseRightReleased(event); SetCursor(wxCURSOR_CROSS); mouseTracker.setVertDragLock(false); mouseTracker.setHorizDragLock(false); mouseZoom = 1.0; } SpectrumVisualDataQueuePtr WaterfallCanvas::getVisualDataQueue() { return visualDataQueue; } void WaterfallCanvas::updateCenterFrequency(long long freq) { if (isView) { setView(freq, getBandwidth()); if (spectrumCanvas) { spectrumCanvas->setView(freq, getBandwidth()); } long long minFreq = wxGetApp().getFrequency()-(wxGetApp().getSampleRate()/2); long long maxFreq = wxGetApp().getFrequency()+(wxGetApp().getSampleRate()/2); if (freq - bandwidth / 2 < minFreq) { wxGetApp().setFrequency(wxGetApp().getFrequency() - (minFreq - (freq - bandwidth/2))); } if (freq + bandwidth / 2 > maxFreq) { wxGetApp().setFrequency(wxGetApp().getFrequency() + ((freq + bandwidth/2) - maxFreq)); } } else { if (spectrumCanvas) { spectrumCanvas->setCenterFrequency(freq); } wxGetApp().setFrequency(freq); } } void WaterfallCanvas::setLinesPerSecond(int lps) { std::lock_guard < std::mutex > lock(tex_update); linesPerSecond = lps; //empty all visualDataQueue->flush(); } void WaterfallCanvas::setMinBandwidth(int min) { minBandwidth = min; } CubicSDR-0.2.3/src/visual/WaterfallCanvas.h000066400000000000000000000053131322677621400204460ustar00rootroot00000000000000// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #pragma once #include "wx/glcanvas.h" #include "wx/timer.h" #include #include #include #include "InteractiveCanvas.h" #include "MouseTracker.h" #include "SpectrumCanvas.h" #include "WaterfallPanel.h" #include "Timer.h" class WaterfallCanvas: public InteractiveCanvas { public: enum DragState { WF_DRAG_NONE, WF_DRAG_BANDWIDTH_LEFT, WF_DRAG_BANDWIDTH_RIGHT, WF_DRAG_FREQUENCY, WF_DRAG_RANGE }; WaterfallCanvas(wxWindow *parent, std::vector dispAttrs); void setup(unsigned int fft_size_in, int waterfall_lines_in); void setFFTSize(unsigned int fft_size_in); ~WaterfallCanvas(); DragState getDragState(); DragState getNextDragState(); void attachSpectrumCanvas(SpectrumCanvas *canvas_in); void processInputQueue(); SpectrumVisualDataQueuePtr getVisualDataQueue(); void setLinesPerSecond(int lps); void setMinBandwidth(int min); //This is public because it is indeed forwarded from //AppFrame::OnGlobalKeyDown, because global key handler intercepts //calls in all windows. void OnKeyDown(wxKeyEvent& event); //This is public because it is indeed forwarded from //AppFrame::OnGlobalKeyUp, because global key handler intercepts //calls in all windows. void OnKeyUp(wxKeyEvent& event); //public because called by SpectrumCanvas. void OnMouseWheelMoved(wxMouseEvent& event); private: void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); void updateHoverState(); void OnMouseMoved(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); void OnMouseRightDown(wxMouseEvent& event); void OnMouseRightReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); void updateCenterFrequency(long long freq); std::vector spectrum_points; SpectrumCanvas *spectrumCanvas = nullptr; PrimaryGLContext *glContext = nullptr; WaterfallPanel waterfallPanel; DragState dragState; DragState nextDragState; unsigned int fft_size, new_fft_size; int waterfall_lines; int dragOfs; float mouseZoom, zoom; bool freqMoving; long double freqMove; float hoverAlpha; int linesPerSecond; float scaleMove; int dragBW; SpectrumVisualDataQueuePtr visualDataQueue = std::make_shared(); Timer gTimer; double lpsIndex; bool preBuf; std::mutex tex_update; int minBandwidth; std::atomic_bool fft_size_changed; // event table wxDECLARE_EVENT_TABLE(); };